From 1a9aa4255f092a609c63a86b53b99f131113ac60 Mon Sep 17 00:00:00 2001 From: AJ Ferguson Date: Thu, 30 Jan 2020 16:05:57 -0900 Subject: [PATCH] Some refactoring and bug fixes Still much to do. Inventory desyncing when crafting will be fixed soon. --- .../network/translators/TranslatorsInit.java | 16 +- ...BedrockInventoryTransactionTranslator.java | 499 +----------------- .../inventory/AnvilInventoryTranslator.java | 63 ++- ...ator.java => BaseInventoryTranslator.java} | 63 +-- .../inventory/BlockInventoryTranslator.java | 71 +-- ...r.java => BrewingInventoryTranslator.java} | 22 +- ....java => CraftingInventoryTranslator.java} | 49 +- .../DoubleChestInventoryTranslator.java | 58 +- .../EnchantmentInventoryTranslator.java | 5 +- .../inventory/FurnaceInventoryTranslator.java | 5 +- .../inventory/InventoryTranslator.java | 9 +- .../inventory/PlayerInventoryTranslator.java | 77 ++- .../SingleChestInventoryTranslator.java | 31 +- .../translators/inventory/SlotType.java | 2 +- .../translators/inventory/action/Click.java | 38 ++ .../inventory/action/ClickPlan.java | 125 +++++ .../action/InventoryActionTranslator.java | 330 ++++++++++++ .../holder/BlockInventoryHolder.java | 90 ++++ .../inventory/holder/InventoryHolder.java | 36 ++ .../updater/ChestInventoryUpdater.java | 72 +++ .../updater/ContainerInventoryUpdater.java | 64 +++ .../updater/CursorInventoryUpdater.java | 64 +++ .../inventory/updater/InventoryUpdater.java | 61 +++ .../network/translators/item/Enchantment.java | 2 +- .../translators/item/ItemTranslator.java | 3 +- .../network/translators/item/Potion.java | 2 +- .../java/JavaDeclareRecipesTranslator.java | 15 +- .../JavaPlayerChangeHeldItemTranslator.java | 45 ++ .../window/JavaCloseWindowTranslator.java | 2 +- .../JavaConfirmTransactionTranslator.java | 2 +- .../java/window/JavaOpenWindowTranslator.java | 2 +- .../java/window/JavaSetSlotTranslator.java | 4 +- .../window/JavaWindowPropertyTranslator.java | 2 +- .../connector/utils/InventoryUtils.java | 34 ++ .../org/geysermc/connector/utils/Toolbox.java | 21 +- 35 files changed, 1282 insertions(+), 702 deletions(-) rename connector/src/main/java/org/geysermc/connector/network/translators/inventory/{ContainerInventoryTranslator.java => BaseInventoryTranslator.java} (50%) rename connector/src/main/java/org/geysermc/connector/network/translators/inventory/{BrewingStandInventoryTranslator.java => BrewingInventoryTranslator.java} (86%) rename connector/src/main/java/org/geysermc/connector/network/translators/inventory/{CraftingTableInventoryTranslator.java => CraftingInventoryTranslator.java} (61%) create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/action/Click.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/action/ClickPlan.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/action/InventoryActionTranslator.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/holder/BlockInventoryHolder.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/holder/InventoryHolder.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/ChestInventoryUpdater.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/ContainerInventoryUpdater.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/CursorInventoryUpdater.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/InventoryUpdater.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerChangeHeldItemTranslator.java 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 538500097..6b3adf15d 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 @@ -46,6 +46,8 @@ import lombok.Getter; import org.geysermc.connector.network.translators.bedrock.*; import org.geysermc.connector.network.translators.block.BlockTranslator; import org.geysermc.connector.network.translators.inventory.*; +import org.geysermc.connector.network.translators.inventory.updater.ContainerInventoryUpdater; +import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater; import org.geysermc.connector.network.translators.item.ItemTranslator; import org.geysermc.connector.network.translators.java.*; import org.geysermc.connector.network.translators.java.entity.*; @@ -72,7 +74,7 @@ public class TranslatorsInit { private static BlockTranslator blockTranslator; @Getter - private static Map inventoryTranslators = new HashMap(); + private static Map inventoryTranslators = new HashMap<>(); private static final CompoundTag EMPTY_TAG = CompoundTagBuilder.builder().buildRootTag(); public static final byte[] EMPTY_LEVEL_CHUNK_DATA; @@ -127,6 +129,7 @@ public class TranslatorsInit { Registry.registerJava(ServerPlayerSetExperiencePacket.class, new JavaPlayerSetExperienceTranslator()); Registry.registerJava(ServerPlayerHealthPacket.class, new JavaPlayerHealthTranslator()); Registry.registerJava(ServerPlayerActionAckPacket.class, new JavaPlayerActionAckTranslator()); + Registry.registerJava(ServerPlayerChangeHeldItemPacket.class, new JavaPlayerChangeHeldItemTranslator()); // FIXME: This translator messes with allowing flight in creative mode. Will need to be addressed later // Registry.registerJava(ServerPlayerAbilitiesPacket.class, new JavaPlayerAbilitiesTranslator()); @@ -176,9 +179,9 @@ public class TranslatorsInit { 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.BREWING_STAND, new BrewingStandInventoryTranslator()); + inventoryTranslators.put(WindowType.BREWING_STAND, new BrewingInventoryTranslator()); inventoryTranslators.put(WindowType.ANVIL, new AnvilInventoryTranslator()); - inventoryTranslators.put(WindowType.CRAFTING, new CraftingTableInventoryTranslator()); + inventoryTranslators.put(WindowType.CRAFTING, new CraftingInventoryTranslator()); //inventoryTranslators.put(WindowType.ENCHANTMENT, new EnchantmentInventoryTranslator()); //TODO InventoryTranslator furnace = new FurnaceInventoryTranslator(); @@ -186,9 +189,10 @@ public class TranslatorsInit { inventoryTranslators.put(WindowType.BLAST_FURNACE, furnace); inventoryTranslators.put(WindowType.SMOKER, furnace); - inventoryTranslators.put(WindowType.GENERIC_3X3, new BlockInventoryTranslator(9, "minecraft:dispenser[facing=north,triggered=false]", ContainerType.DISPENSER)); - inventoryTranslators.put(WindowType.HOPPER, new BlockInventoryTranslator(5, "minecraft:hopper[enabled=false,facing=down]", ContainerType.HOPPER)); - inventoryTranslators.put(WindowType.SHULKER_BOX, new BlockInventoryTranslator(27, "minecraft:shulker_box[facing=north]", ContainerType.CONTAINER)); + InventoryUpdater containerUpdater = new ContainerInventoryUpdater(); + inventoryTranslators.put(WindowType.GENERIC_3X3, new BlockInventoryTranslator(9, "minecraft:dispenser[facing=north,triggered=false]", ContainerType.DISPENSER, containerUpdater)); + inventoryTranslators.put(WindowType.HOPPER, new BlockInventoryTranslator(5, "minecraft:hopper[enabled=false,facing=down]", ContainerType.HOPPER, containerUpdater)); + inventoryTranslators.put(WindowType.SHULKER_BOX, new BlockInventoryTranslator(27, "minecraft:shulker_box[facing=north]", ContainerType.CONTAINER, containerUpdater)); //inventoryTranslators.put(WindowType.BEACON, new BlockInventoryTranslator(1, "minecraft:beacon", ContainerType.BEACON)); //TODO } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java index ca6729b96..ee12b5aad 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java @@ -25,13 +25,7 @@ package org.geysermc.connector.network.translators.bedrock; -import com.github.steveice10.mc.protocol.data.game.window.*; -import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientConfirmTransactionPacket; -import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientCreativeInventoryActionPacket; -import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientRenameItemPacket; -import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.nukkitx.math.vector.Vector3f; -import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; import com.github.steveice10.mc.protocol.data.game.entity.player.Hand; @@ -41,382 +35,31 @@ import com.github.steveice10.mc.protocol.data.game.world.block.BlockFace; import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerActionPacket; import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerInteractEntityPacket; import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerUseItemPacket; -import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientWindowActionPacket; -import com.nukkitx.protocol.bedrock.data.ContainerId; -import com.nukkitx.protocol.bedrock.data.InventoryAction; -import com.nukkitx.protocol.bedrock.data.InventorySource; -import com.nukkitx.protocol.bedrock.data.ItemData; -import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket; import com.nukkitx.protocol.bedrock.packet.InventoryTransactionPacket; import org.geysermc.connector.entity.Entity; import org.geysermc.connector.inventory.Inventory; -import org.geysermc.connector.inventory.PlayerInventory; 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.network.translators.inventory.SlotType; +import org.geysermc.connector.utils.InventoryUtils; import java.util.*; public class BedrockInventoryTransactionTranslator extends PacketTranslator { - private final ItemStack refreshItem = new ItemStack(1, 127, new CompoundTag("")); @Override public void translate(InventoryTransactionPacket packet, GeyserSession session) { switch (packet.getTransactionType()) { case NORMAL: - for (InventoryAction action : packet.getActions()) { - if (action.getSource().getContainerId() == ContainerId.CRAFTING_USE_INGREDIENT || - action.getSource().getContainerId() == ContainerId.CRAFTING_RESULT) { - return; - } - } - Inventory inventory = session.getInventoryCache().getOpenInventory(); - if (inventory == null) - inventory = session.getInventory(); - InventoryTranslator translator = TranslatorsInit.getInventoryTranslators().get(inventory.getWindowType()); - - int craftSlot = session.getCraftSlot(); - session.setCraftSlot(0); - - if (session.getGameMode() == GameMode.CREATIVE && inventory.getId() == 0) { - ItemStack javaItem; - for (InventoryAction action : packet.getActions()) { - switch (action.getSource().getContainerId()) { - case ContainerId.INVENTORY: - case ContainerId.ARMOR: - case ContainerId.OFFHAND: - int javaSlot = translator.bedrockSlotToJava(action); - if (action.getToItem().getId() == 0) { - javaItem = new ItemStack(-1, 0, null); - } else { - javaItem = TranslatorsInit.getItemTranslator().translateToJava(action.getToItem()); - if (javaItem.getId() == 0) { //item missing mapping - translator.updateInventory(session, inventory); - break; - } - } - ClientCreativeInventoryActionPacket creativePacket = new ClientCreativeInventoryActionPacket(javaSlot, fixStack(javaItem)); - session.getDownstream().getSession().send(creativePacket); - inventory.setItem(javaSlot, javaItem); - break; - case ContainerId.NONE: - if (action.getSource().getType() == InventorySource.Type.WORLD_INTERACTION && - action.getSource().getFlag() == InventorySource.Flag.DROP_ITEM) { - javaItem = TranslatorsInit.getItemTranslator().translateToJava(action.getToItem()); - if (javaItem.getId() == 0) { //item missing mapping - break; - } - ClientCreativeInventoryActionPacket creativeDropPacket = new ClientCreativeInventoryActionPacket(-1, fixStack(javaItem)); - session.getDownstream().getSession().send(creativeDropPacket); - } - break; - } - } - return; - } - - InventoryAction worldAction = null; - InventoryAction cursorAction = null; - for (InventoryAction action : packet.getActions()) { - if (action.getSource().getType() == InventorySource.Type.WORLD_INTERACTION) { - worldAction = action; - } else if (action.getSource().getContainerId() == ContainerId.CURSOR && action.getSlot() == 0) { - cursorAction = action; - } - } - List actions = packet.getActions(); - if (inventory.getWindowType() == WindowType.ANVIL) { - InventoryAction anvilResult = null; - InventoryAction anvilInput = null; - for (InventoryAction action : packet.getActions()) { - if (action.getSource().getContainerId() == ContainerId.ANVIL_MATERIAL) { - //useless packet - return; - } else if (action.getSource().getContainerId() == ContainerId.ANVIL_RESULT) { - anvilResult = action; - } else if (translator.bedrockSlotToJava(action) == 0) { - anvilInput = action; - } - } - ItemData itemName = null; - if (anvilResult != null) { - itemName = anvilResult.getFromItem(); - } else if (anvilInput != null) { - itemName = anvilInput.getToItem(); - } - if (itemName != null) { - String rename; - com.nukkitx.nbt.tag.CompoundTag tag = itemName.getTag(); - if (tag != null) { - rename = tag.getAsCompound("display").getAsString("Name"); - } else { - rename = ""; - } - ClientRenameItemPacket renameItemPacket = new ClientRenameItemPacket(rename); - session.getDownstream().getSession().send(renameItemPacket); - } - if (anvilResult != null) { - //client will send another packet to grab anvil output - //this packet was only used to send rename packet - return; - } - } - - if (actions.size() == 2) { - if (worldAction != null) { - //find container action - InventoryAction containerAction = null; - for (InventoryAction action : actions) { - if (action != worldAction) { - containerAction = action; - break; - } - } - if (containerAction != null && worldAction.getSource().getFlag() == InventorySource.Flag.DROP_ITEM) { - //quick dropping from hotbar? - if (session.getInventoryCache().getOpenInventory() == null && containerAction.getSource().getContainerId() == ContainerId.INVENTORY) { - int heldSlot = session.getInventory().getHeldItemSlot(); - if (containerAction.getSlot() == heldSlot) { - ClientPlayerActionPacket actionPacket = new ClientPlayerActionPacket( - containerAction.getToItem().getCount() == 0 ? PlayerAction.DROP_ITEM_STACK : PlayerAction.DROP_ITEM, - new Position(0, 0, 0), BlockFace.DOWN); - session.getDownstream().getSession().send(actionPacket); - ItemStack item = session.getInventory().getItem(heldSlot); - if (item != null) { - session.getInventory().setItem(heldSlot, new ItemStack(item.getId(), item.getAmount() - 1, item.getNbt())); - } - return; - } - } - int dropAmount = containerAction.getFromItem().getCount() - containerAction.getToItem().getCount(); - if (containerAction != cursorAction) { //dropping directly from inventory - int javaSlot = translator.bedrockSlotToJava(containerAction); - if (dropAmount == containerAction.getFromItem().getCount()) { - ClientWindowActionPacket dropPacket = new ClientWindowActionPacket(inventory.getId(), - inventory.getTransactionId().getAndIncrement(), - javaSlot, null, WindowAction.DROP_ITEM, - DropItemParam.DROP_SELECTED_STACK); - session.getDownstream().getSession().send(dropPacket); - } else { - for (int i = 0; i < dropAmount; i++) { - ClientWindowActionPacket dropPacket = new ClientWindowActionPacket(inventory.getId(), - inventory.getTransactionId().getAndIncrement(), - javaSlot, null, WindowAction.DROP_ITEM, - DropItemParam.DROP_FROM_SELECTED); - session.getDownstream().getSession().send(dropPacket); - } - } - ItemStack item = session.getInventory().getItem(javaSlot); - if (item != null) { - session.getInventory().setItem(javaSlot, new ItemStack(item.getId(), item.getAmount() - dropAmount, item.getNbt())); - } - return; - } else { //clicking outside of inventory - ClientWindowActionPacket dropPacket = new ClientWindowActionPacket(inventory.getId(), inventory.getTransactionId().getAndIncrement(), - -999, null, WindowAction.CLICK_ITEM, - dropAmount > 1 ? ClickItemParam.LEFT_CLICK : ClickItemParam.RIGHT_CLICK); - session.getDownstream().getSession().send(dropPacket); - ItemStack cursor = session.getInventory().getCursor(); - if (cursor != null) { - session.getInventory().setCursor(new ItemStack(cursor.getId(), dropAmount > 1 ? 0 : cursor.getAmount() - 1, cursor.getNbt())); - } - return; - } - } - } else if (cursorAction != null) { - //find container action - InventoryAction containerAction = null; - for (InventoryAction action : actions) { - if (action != cursorAction) { - containerAction = action; - break; - } - } - if (containerAction != null) { - //left/right click - List plan = new ArrayList<>(); - ItemStack translatedCursor = cursorAction.getFromItem().isValid() ? - TranslatorsInit.getItemTranslator().translateToJava(cursorAction.getFromItem()) : null; - ItemStack currentCursor = session.getInventory().getCursor(); - boolean refresh = false; - if (currentCursor != null) { - if (translatedCursor != null) { - refresh = !(currentCursor.getId() == translatedCursor.getId() && - currentCursor.getAmount() == translatedCursor.getAmount()); - } else { - refresh = true; - } - } - - int javaSlot = translator.bedrockSlotToJava(containerAction); - if (cursorAction.getFromItem().equals(containerAction.getToItem()) && - containerAction.getFromItem().equals(cursorAction.getToItem()) && - !canStack(cursorAction.getFromItem(), containerAction.getFromItem())) { //simple swap - Click.LEFT.onSlot(javaSlot, plan); - } else if (cursorAction.getFromItem().getCount() > cursorAction.getToItem().getCount()) { //release - if (cursorAction.getToItem().getCount() == 0) { - Click.LEFT.onSlot(javaSlot, plan); - } else { - int difference = cursorAction.getFromItem().getCount() - cursorAction.getToItem().getCount(); - for (int i = 0; i < difference; i++) { - Click.RIGHT.onSlot(javaSlot, plan); - } - } - } else { //pickup - if (cursorAction.getFromItem().getCount() == 0) { - if (containerAction.getToItem().getCount() == 0) { //pickup all - Click.LEFT.onSlot(javaSlot, plan); - } else { //pickup some - if (translator.getSlotType(javaSlot) == SlotType.FURNACE_OUTPUT || - containerAction.getToItem().getCount() == containerAction.getFromItem().getCount() / 2) { //right click - Click.RIGHT.onSlot(javaSlot, plan); - } else { - Click.LEFT.onSlot(javaSlot, plan); - int difference = containerAction.getFromItem().getCount() - cursorAction.getToItem().getCount(); - for (int i = 0; i < difference; i++) { - Click.RIGHT.onSlot(javaSlot, plan); - } - } - } - } else { //pickup into non-empty cursor - if (translator.getSlotType(javaSlot) == SlotType.FURNACE_OUTPUT) { - if (containerAction.getToItem().getCount() == 0) { - Click.LEFT.onSlot(javaSlot, plan); - } else { - ClientWindowActionPacket shiftClickPacket = new ClientWindowActionPacket(inventory.getId(), - inventory.getTransactionId().getAndIncrement(), - javaSlot, refreshItem, WindowAction.SHIFT_CLICK_ITEM, - ShiftClickItemParam.LEFT_CLICK); - session.getDownstream().getSession().send(shiftClickPacket); - translator.updateInventory(session, inventory); - return; - } - } else if (translator.getSlotType(javaSlot) == SlotType.OUTPUT) { - Click.LEFT.onSlot(javaSlot, plan); - } else { - int cursorSlot = findTempSlot(inventory, session.getInventory().getCursor(), Collections.singletonList(javaSlot)); - if (cursorSlot != -1) { - Click.LEFT.onSlot(cursorSlot, plan); - } else { - translator.updateInventory(session, inventory); - return; - } - Click.LEFT.onSlot(javaSlot, plan); - int difference = cursorAction.getToItem().getCount() - cursorAction.getFromItem().getCount(); - for (int i = 0; i < difference; i++) { - Click.RIGHT.onSlot(cursorSlot, plan); - } - Click.LEFT.onSlot(javaSlot, plan); - Click.LEFT.onSlot(cursorSlot, plan); - } - } - } - executePlan(session, inventory, translator, plan, refresh); - return; - } - } else { - List plan = new ArrayList<>(); - InventoryAction fromAction; - InventoryAction toAction; - if (actions.get(0).getFromItem().getCount() >= actions.get(0).getToItem().getCount()) { - fromAction = actions.get(0); - toAction = actions.get(1); - } else { - fromAction = actions.get(1); - toAction = actions.get(0); - } - int fromSlot = translator.bedrockSlotToJava(fromAction); - int toSlot = translator.bedrockSlotToJava(toAction); - - if (translator.getSlotType(fromSlot) == SlotType.OUTPUT) { - if ((craftSlot != 0 && craftSlot != -2) && (inventory.getItem(toSlot) == null || - canStack(session.getInventory().getCursor(), inventory.getItem(toSlot)))) { - boolean refresh = false; - if (fromAction.getToItem().getCount() == 0) { - refresh = true; - Click.LEFT.onSlot(toSlot, plan); - if (craftSlot != -1) { - Click.LEFT.onSlot(craftSlot, plan); - } - } else { - int difference = toAction.getToItem().getCount() - toAction.getFromItem().getCount(); - for (int i = 0; i < difference; i++) { - Click.RIGHT.onSlot(toSlot, plan); - } - session.setCraftSlot(craftSlot); - } - executePlan(session, inventory, translator, plan, refresh); - return; - } else { - session.setCraftSlot(-2); - } - } - - int cursorSlot = -1; - if (session.getInventory().getCursor() != null) { //move cursor contents to a temporary slot - cursorSlot = findTempSlot(inventory, session.getInventory().getCursor(), Arrays.asList(fromSlot, toSlot)); - if (cursorSlot != -1) { - Click.LEFT.onSlot(cursorSlot, plan); - } else { - translator.updateInventory(session, inventory); - return; - } - } - if ((fromAction.getFromItem().equals(toAction.getToItem()) && !canStack(fromAction.getFromItem(), toAction.getFromItem())) || fromAction.getToItem().getId() == 0) { //slot swap - Click.LEFT.onSlot(fromSlot, plan); - Click.LEFT.onSlot(toSlot, plan); - if (fromAction.getToItem().getId() != 0) { - Click.LEFT.onSlot(fromSlot, plan); - } - } else if (canStack(fromAction.getFromItem(), toAction.getToItem())) { //partial item move - if (translator.getSlotType(fromSlot) == SlotType.FURNACE_OUTPUT) { - ClientWindowActionPacket shiftClickPacket = new ClientWindowActionPacket(inventory.getId(), - inventory.getTransactionId().getAndIncrement(), - fromSlot, refreshItem, WindowAction.SHIFT_CLICK_ITEM, - ShiftClickItemParam.LEFT_CLICK); - session.getDownstream().getSession().send(shiftClickPacket); - translator.updateInventory(session, inventory); - return; - } else if (translator.getSlotType(fromSlot) == SlotType.OUTPUT) { - session.setCraftSlot(cursorSlot); - Click.LEFT.onSlot(fromSlot, plan); - int difference = toAction.getToItem().getCount() - toAction.getFromItem().getCount(); - for (int i = 0; i < difference; i++) { - Click.RIGHT.onSlot(toSlot, plan); - } - //client will send additional packets later to finish transferring crafting output - //translator will know how to handle this using the craftSlot variable - } else { - Click.LEFT.onSlot(fromSlot, plan); - int difference = toAction.getToItem().getCount() - toAction.getFromItem().getCount(); - for (int i = 0; i < difference; i++) { - Click.RIGHT.onSlot(toSlot, plan); - } - Click.LEFT.onSlot(fromSlot, plan); - } - } - if (cursorSlot != -1) { - Click.LEFT.onSlot(cursorSlot, plan); - } - executePlan(session, inventory, translator, plan, false); - return; - } - } - translator.updateInventory(session, inventory); + if (inventory == null) inventory = session.getInventory(); + TranslatorsInit.getInventoryTranslators().get(inventory.getWindowType()).translateActions(session, inventory, packet.getActions()); break; case INVENTORY_MISMATCH: - InventorySlotPacket cursorPacket = new InventorySlotPacket(); - cursorPacket.setContainerId(ContainerId.CURSOR); - cursorPacket.setSlot(TranslatorsInit.getItemTranslator().translateToBedrock(session.getInventory().getCursor())); - //session.getUpstream().sendPacket(cursorPacket); - Inventory inv = session.getInventoryCache().getOpenInventory(); - if (inv == null) - inv = session.getInventory(); + if (inv == null) inv = session.getInventory(); TranslatorsInit.getInventoryTranslators().get(inv.getWindowType()).updateInventory(session, inv); + InventoryUtils.updateCursor(session); break; case ITEM_USE: if (packet.getActionType() == 1) { @@ -448,136 +91,4 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator slotBlacklist) { - /*try and find a slot that can temporarily store the given item - only look in the main inventory and hotbar - only slots that are empty or contain a different type of item are valid*/ - int offset = inventory.getId() == 0 ? 1 : 0; //offhand is not a viable slot (some servers disable it) - List itemBlacklist = new ArrayList<>(slotBlacklist.size() + 1); - itemBlacklist.add(item); - for (int slot : slotBlacklist) { - ItemStack blacklistItem = inventory.getItem(slot); - if (blacklistItem != null) - itemBlacklist.add(blacklistItem); - } - for (int i = inventory.getSize() - (36 + offset); i < inventory.getSize() - offset; i++) { - ItemStack testItem = inventory.getItem(i); - boolean acceptable = true; - if (testItem != null) { - for (ItemStack blacklistItem : itemBlacklist) { - if (canStack(testItem, blacklistItem)) { - acceptable = false; - break; - } - } - } - if (acceptable && !slotBlacklist.contains(i)) - return i; - } - //could not find a viable temp slot - return -1; - } - - //NPE if compound tag is null - private ItemStack fixStack(ItemStack stack) { - if (stack == null || stack.getId() == 0) - return null; - return new ItemStack(stack.getId(), stack.getAmount(), stack.getNbt() == null ? new CompoundTag("") : stack.getNbt()); - } - - private boolean canStack(ItemStack item1, ItemStack item2) { - if (item1 == null || item2 == null) - return false; - return item1.getId() == item2.getId() && Objects.equals(item1.getNbt(), item2.getNbt()); - } - - private boolean canStack(ItemData item1, ItemData item2) { - if (item1 == null || item2 == null) - return false; - return item1.equals(item2, false, true, true); - } - - private void executePlan(GeyserSession session, Inventory inventory, InventoryTranslator translator, List plan, boolean refresh) { - PlayerInventory playerInventory = session.getInventory(); - ListIterator planIter = plan.listIterator(); - while (planIter.hasNext()) { - ClickAction action = planIter.next(); - ItemStack cursorItem = playerInventory.getCursor(); - ItemStack clickedItem = inventory.getItem(action.slot); - short actionId = (short) inventory.getTransactionId().getAndIncrement(); - boolean isOutput = translator.getSlotType(action.slot) == SlotType.OUTPUT; - - if (isOutput || translator.getSlotType(action.slot) == SlotType.FURNACE_OUTPUT) - refresh = true; - ClientWindowActionPacket clickPacket = new ClientWindowActionPacket(inventory.getId(), - actionId, action.slot, !planIter.hasNext() && refresh ? refreshItem : fixStack(clickedItem), - WindowAction.CLICK_ITEM, action.click.actionParam); - - if (isOutput) { - if (cursorItem == null && clickedItem != null) { - playerInventory.setCursor(clickedItem); - } else if (canStack(cursorItem, clickedItem)) { - playerInventory.setCursor(new ItemStack(cursorItem.getId(), - cursorItem.getAmount() + clickedItem.getAmount(), cursorItem.getNbt())); - } - } else { - switch (action.click) { - case LEFT: - if (!canStack(cursorItem, clickedItem)) { - playerInventory.setCursor(clickedItem); - inventory.setItem(action.slot, cursorItem); - } else { - playerInventory.setCursor(null); - inventory.setItem(action.slot, new ItemStack(clickedItem.getId(), - clickedItem.getAmount() + cursorItem.getAmount(), clickedItem.getNbt())); - } - break; - case RIGHT: - if (cursorItem == null && clickedItem != null) { - ItemStack halfItem = new ItemStack(clickedItem.getId(), - clickedItem.getAmount() / 2, clickedItem.getNbt()); - inventory.setItem(action.slot, halfItem); - playerInventory.setCursor(new ItemStack(clickedItem.getId(), - clickedItem.getAmount() - halfItem.getAmount(), clickedItem.getNbt())); - } else if (cursorItem != null && clickedItem == null) { - playerInventory.setCursor(new ItemStack(cursorItem.getId(), - cursorItem.getAmount() - 1, cursorItem.getNbt())); - inventory.setItem(action.slot, new ItemStack(cursorItem.getId(), - 1, cursorItem.getNbt())); - } else if (canStack(cursorItem, clickedItem)) { - playerInventory.setCursor(new ItemStack(cursorItem.getId(), - cursorItem.getAmount() - 1, cursorItem.getNbt())); - inventory.setItem(action.slot, new ItemStack(clickedItem.getId(), - clickedItem.getAmount() + 1, clickedItem.getNbt())); - } - break; - } - } - session.getDownstream().getSession().send(clickPacket); - session.getDownstream().getSession().send(new ClientConfirmTransactionPacket(inventory.getId(), actionId, true)); - } - } - - private enum Click { - LEFT(ClickItemParam.LEFT_CLICK), - RIGHT(ClickItemParam.RIGHT_CLICK); - - final WindowActionParam actionParam; - Click(WindowActionParam actionParam) { - this.actionParam = actionParam; - } - void onSlot(int slot, List plan) { - plan.add(new ClickAction(slot, this)); - } - } - - private static class ClickAction { - final int slot; - final Click click; - ClickAction(int slot, Click click) { - this.slot = slot; - this.click = click; - } - } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/AnvilInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/AnvilInventoryTranslator.java index 115a7dbd7..395751888 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/AnvilInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/AnvilInventoryTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2020 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 @@ -25,13 +25,20 @@ package org.geysermc.connector.network.translators.inventory; +import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientRenameItemPacket; import com.nukkitx.protocol.bedrock.data.ContainerId; import com.nukkitx.protocol.bedrock.data.ContainerType; import com.nukkitx.protocol.bedrock.data.InventoryAction; +import com.nukkitx.protocol.bedrock.data.ItemData; +import org.geysermc.connector.inventory.Inventory; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.inventory.updater.CursorInventoryUpdater; + +import java.util.List; public class AnvilInventoryTranslator extends BlockInventoryTranslator { public AnvilInventoryTranslator() { - super(3, "minecraft:anvil[facing=north]", ContainerType.ANVIL); + super(3, "minecraft:anvil[facing=north]", ContainerType.ANVIL, new CursorInventoryUpdater()); } @Override @@ -49,10 +56,62 @@ public class AnvilInventoryTranslator extends BlockInventoryTranslator { return super.bedrockSlotToJava(action); } + @Override + public int javaSlotToBedrock(int slot) { + switch (slot) { + case 0: + return 1; + case 1: + return 2; + case 2: + return 50; + } + return super.javaSlotToBedrock(slot); + } + @Override public SlotType getSlotType(int javaSlot) { if (javaSlot == 2) return SlotType.OUTPUT; return SlotType.NORMAL; } + + @Override + public void translateActions(GeyserSession session, Inventory inventory, List actions) { + InventoryAction anvilResult = null; + InventoryAction anvilInput = null; + for (InventoryAction action : actions) { + if (action.getSource().getContainerId() == ContainerId.ANVIL_MATERIAL) { + //useless packet + return; + } else if (action.getSource().getContainerId() == ContainerId.ANVIL_RESULT) { + anvilResult = action; + } else if (bedrockSlotToJava(action) == 0) { + anvilInput = action; + } + } + ItemData itemName = null; + if (anvilResult != null) { + itemName = anvilResult.getFromItem(); + } else if (anvilInput != null) { + itemName = anvilInput.getToItem(); + } + if (itemName != null) { + String rename; + com.nukkitx.nbt.tag.CompoundTag tag = itemName.getTag(); + if (tag != null) { + rename = tag.getAsCompound("display").getAsString("Name"); + } else { + rename = ""; + } + ClientRenameItemPacket renameItemPacket = new ClientRenameItemPacket(rename); + session.getDownstream().getSession().send(renameItemPacket); + } + if (anvilResult != null) { + //client will send another packet to grab anvil output + return; + } + + super.translateActions(session, inventory, actions); + } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/ContainerInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/BaseInventoryTranslator.java similarity index 50% rename from connector/src/main/java/org/geysermc/connector/network/translators/inventory/ContainerInventoryTranslator.java rename to connector/src/main/java/org/geysermc/connector/network/translators/inventory/BaseInventoryTranslator.java index 8f66675b1..d64c0e78e 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/ContainerInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/BaseInventoryTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2020 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 @@ -27,53 +27,20 @@ package org.geysermc.connector.network.translators.inventory; import com.nukkitx.protocol.bedrock.data.ContainerId; import com.nukkitx.protocol.bedrock.data.InventoryAction; -import com.nukkitx.protocol.bedrock.data.ItemData; -import com.nukkitx.protocol.bedrock.packet.InventoryContentPacket; -import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket; 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.action.InventoryActionTranslator; -public abstract class ContainerInventoryTranslator extends InventoryTranslator { - ContainerInventoryTranslator(int size) { +import java.util.List; + +public abstract class BaseInventoryTranslator extends InventoryTranslator{ + BaseInventoryTranslator(int size) { super(size); } - @Override - public void updateInventory(GeyserSession session, Inventory inventory) { - ItemData[] bedrockItems = new ItemData[this.size]; - for (int i = 0; i < bedrockItems.length; i++) { - bedrockItems[javaSlotToBedrock(i)] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(i)); - } - 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.setItem(i + 9, inventory.getItem(i + this.size)); - } - TranslatorsInit.getInventoryTranslators().get(playerInventory.getWindowType()).updateInventory(session, playerInventory); - } - - @Override - public void updateSlot(GeyserSession session, Inventory inventory, int slot) { - if (slot >= this.size) { - Inventory playerInventory = session.getInventory(); - playerInventory.setItem((slot + 9) - this.size, inventory.getItem(slot)); - TranslatorsInit.getInventoryTranslators().get(playerInventory.getWindowType()).updateSlot(session, playerInventory, (slot + 9) - this.size); - } else { - InventorySlotPacket slotPacket = new InventorySlotPacket(); - slotPacket.setContainerId(inventory.getId()); - slotPacket.setInventorySlot(javaSlotToBedrock(slot)); - slotPacket.setSlot(TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(slot))); - session.getUpstream().sendPacket(slotPacket); - } - } - @Override public void updateProperty(GeyserSession session, Inventory inventory, int key, int value) { + // } @Override @@ -86,13 +53,20 @@ public abstract class ContainerInventoryTranslator extends InventoryTranslator { } else { return slotnum + this.size + 27; } - } else { - return slotnum; } + return slotnum; } @Override public int javaSlotToBedrock(int slot) { + if (slot >= this.size) { + final int tmp = slot - this.size; + if (tmp < 27) { + return tmp + 9; + } else { + return tmp - 27; + } + } return slot; } @@ -100,4 +74,9 @@ public abstract class ContainerInventoryTranslator extends InventoryTranslator { public SlotType getSlotType(int javaSlot) { return SlotType.NORMAL; } + + @Override + public void translateActions(GeyserSession session, Inventory inventory, List actions) { + InventoryActionTranslator.translate(this, session, inventory, actions); + } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/BlockInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/BlockInventoryTranslator.java index 54ab9bcec..32dfc2a63 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/BlockInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/BlockInventoryTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2020 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 @@ -25,70 +25,47 @@ 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.packet.BlockEntityDataPacket; -import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket; -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.network.translators.inventory.holder.BlockInventoryHolder; +import org.geysermc.connector.network.translators.inventory.holder.InventoryHolder; +import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater; -public class BlockInventoryTranslator extends ContainerInventoryTranslator { - final int blockId; - private final ContainerType containerType; +public class BlockInventoryTranslator extends BaseInventoryTranslator { + private final InventoryHolder holder; + private final InventoryUpdater updater; - public BlockInventoryTranslator(int size, String javaBlockIdentifier, ContainerType containerType) { + public BlockInventoryTranslator(int size, String javaBlockIdentifier, ContainerType containerType, InventoryUpdater updater) { super(size); - this.blockId = TranslatorsInit.getBlockTranslator().getBlockEntry(javaBlockIdentifier).getBedrockRuntimeId(); - this.containerType = containerType; + final int blockId = TranslatorsInit.getBlockTranslator().getBlockEntry(javaBlockIdentifier).getBedrockRuntimeId(); + this.holder = new BlockInventoryHolder(blockId, containerType); + this.updater = updater; } @Override public void prepareInventory(GeyserSession session, Inventory inventory) { - Vector3i position = session.getPlayerEntity().getPosition().toInt(); - position = position.add(Vector3i.UP); - UpdateBlockPacket blockPacket = new UpdateBlockPacket(); - blockPacket.setDataLayer(0); - blockPacket.setBlockPosition(position); - blockPacket.setRuntimeId(blockId); - blockPacket.getFlags().add(UpdateBlockPacket.Flag.PRIORITY); - session.getUpstream().sendPacket(blockPacket); - inventory.setHolderPosition(position); - - CompoundTag tag = CompoundTag.EMPTY.toBuilder() - .intTag("x", position.getX()) - .intTag("y", position.getY()) - .intTag("z", position.getZ()) - .stringTag("CustomName", inventory.getTitle()).buildRootTag(); - BlockEntityDataPacket dataPacket = new BlockEntityDataPacket(); - dataPacket.setData(tag); - dataPacket.setBlockPosition(position); - session.getUpstream().sendPacket(dataPacket); + holder.prepareInventory(this, session, inventory); } @Override public void openInventory(GeyserSession session, Inventory inventory) { - ContainerOpenPacket containerOpenPacket = new ContainerOpenPacket(); - containerOpenPacket.setWindowId((byte) inventory.getId()); - containerOpenPacket.setType((byte) containerType.id()); - containerOpenPacket.setBlockPosition(inventory.getHolderPosition()); - containerOpenPacket.setUniqueEntityId(inventory.getHolderId()); - session.getUpstream().sendPacket(containerOpenPacket); + holder.openInventory(this, session, inventory); } @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(realBlock.getBedrockRuntimeId()); - session.getUpstream().sendPacket(blockPacket); + holder.closeInventory(this, session, inventory); + } + + @Override + public void updateInventory(GeyserSession session, Inventory inventory) { + updater.updateInventory(this, session, inventory); + } + + @Override + public void updateSlot(GeyserSession session, Inventory inventory, int slot) { + updater.updateSlot(this, session, inventory, slot); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/BrewingStandInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/BrewingInventoryTranslator.java similarity index 86% rename from connector/src/main/java/org/geysermc/connector/network/translators/inventory/BrewingStandInventoryTranslator.java rename to connector/src/main/java/org/geysermc/connector/network/translators/inventory/BrewingInventoryTranslator.java index 532928e64..c5f67a03a 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/BrewingStandInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/BrewingInventoryTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2020 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 @@ -30,10 +30,11 @@ import com.nukkitx.protocol.bedrock.data.InventoryAction; import com.nukkitx.protocol.bedrock.packet.ContainerSetDataPacket; import org.geysermc.connector.inventory.Inventory; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.inventory.updater.ContainerInventoryUpdater; -public class BrewingStandInventoryTranslator extends BlockInventoryTranslator { - public BrewingStandInventoryTranslator() { - super(5, "minecraft:brewing_stand[has_bottle_0=false,has_bottle_1=false,has_bottle_2=false]", ContainerType.BREWING_STAND); +public class BrewingInventoryTranslator extends BlockInventoryTranslator { + public BrewingInventoryTranslator() { + super(5, "minecraft:brewing_stand[has_bottle_0=false,has_bottle_1=false,has_bottle_2=false]", ContainerType.BREWING_STAND, new ContainerInventoryUpdater()); } @Override @@ -66,8 +67,8 @@ public class BrewingStandInventoryTranslator extends BlockInventoryTranslator { @Override public int bedrockSlotToJava(InventoryAction action) { - int slotnum = super.bedrockSlotToJava(action); - switch (slotnum) { + final int slot = super.bedrockSlotToJava(action); + switch (slot) { case 0: return 3; case 1: @@ -77,13 +78,13 @@ public class BrewingStandInventoryTranslator extends BlockInventoryTranslator { case 3: return 2; default: - return slotnum; + return slot; } } @Override - public int javaSlotToBedrock(int slotnum) { - switch (slotnum) { + public int javaSlotToBedrock(int slot) { + switch (slot) { case 0: return 1; case 1: @@ -92,8 +93,7 @@ public class BrewingStandInventoryTranslator extends BlockInventoryTranslator { return 3; case 3: return 0; - default: - return slotnum; } + return super.javaSlotToBedrock(slot); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/CraftingTableInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/CraftingInventoryTranslator.java similarity index 61% rename from connector/src/main/java/org/geysermc/connector/network/translators/inventory/CraftingTableInventoryTranslator.java rename to connector/src/main/java/org/geysermc/connector/network/translators/inventory/CraftingInventoryTranslator.java index 28ad0cb20..fe70609fe 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/CraftingTableInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/CraftingInventoryTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2020 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 @@ -25,21 +25,33 @@ package org.geysermc.connector.network.translators.inventory; +import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; import com.nukkitx.protocol.bedrock.data.ContainerId; import com.nukkitx.protocol.bedrock.data.ContainerType; import com.nukkitx.protocol.bedrock.data.InventoryAction; +import com.nukkitx.protocol.bedrock.data.InventorySource; import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket; +import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket; 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.updater.CursorInventoryUpdater; +import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater; +import org.geysermc.connector.utils.InventoryUtils; -public class CraftingTableInventoryTranslator extends ContainerInventoryTranslator { - public CraftingTableInventoryTranslator() { +import java.util.List; + +public class CraftingInventoryTranslator extends BaseInventoryTranslator { + private final InventoryUpdater updater; + + public CraftingInventoryTranslator() { super(10); + this.updater = new CursorInventoryUpdater(); } @Override public void prepareInventory(GeyserSession session, Inventory inventory) { - + // } @Override @@ -54,7 +66,17 @@ public class CraftingTableInventoryTranslator extends ContainerInventoryTranslat @Override public void closeInventory(GeyserSession session, Inventory inventory) { + // + } + @Override + public void updateInventory(GeyserSession session, Inventory inventory) { + updater.updateInventory(this, session, inventory); + } + + @Override + public void updateSlot(GeyserSession session, Inventory inventory, int slot) { + updater.updateSlot(this, session, inventory, slot); } @Override @@ -70,10 +92,29 @@ public class CraftingTableInventoryTranslator extends ContainerInventoryTranslat return super.bedrockSlotToJava(action); } + @Override + public int javaSlotToBedrock(int slot) { + return slot == 0 ? 50 : slot + 31; + } + @Override public SlotType getSlotType(int javaSlot) { if (javaSlot == 0) return SlotType.OUTPUT; return SlotType.NORMAL; } + + @Override + public void translateActions(GeyserSession session, Inventory inventory, List actions) { + if (session.getGameMode() == GameMode.CREATIVE) { + for (InventoryAction action : actions) { + if (action.getSource().getType() == InventorySource.Type.CREATIVE) { + updateInventory(session, inventory); + InventoryUtils.updateCursor(session); + return; + } + } + } + super.translateActions(session, inventory, actions); + } } 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 index e0825ed87..07d3a414e 100644 --- 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2020 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 @@ -29,32 +29,39 @@ 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.ItemData; import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket; -import com.nukkitx.protocol.bedrock.packet.InventoryContentPacket; +import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket; 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.network.translators.inventory.updater.ChestInventoryUpdater; +import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater; + +public class DoubleChestInventoryTranslator extends BaseInventoryTranslator { + private final int blockId; + private final InventoryUpdater updater; -public class DoubleChestInventoryTranslator extends BlockInventoryTranslator { public DoubleChestInventoryTranslator(int size) { - super(size, "minecraft:chest[facing=north,type=single,waterlogged=false]", ContainerType.CONTAINER); + super(size); + this.blockId = TranslatorsInit.getBlockTranslator().getBlockEntry("minecraft:chest[facing=north,type=single,waterlogged=false]").getBedrockRuntimeId(); + this.updater = new ChestInventoryUpdater(54); } @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(blockId); - blockPacket.getFlags().add(UpdateBlockPacket.Flag.PRIORITY); + blockPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY); session.getUpstream().sendPacket(blockPacket); - CompoundTag tag = CompoundTag.EMPTY.toBuilder() + CompoundTag tag = CompoundTag.builder() .stringTag("id", "Chest") .intTag("x", position.getX()) .intTag("y", position.getY()) @@ -71,10 +78,10 @@ public class DoubleChestInventoryTranslator extends BlockInventoryTranslator { blockPacket.setDataLayer(0); blockPacket.setBlockPosition(pairPosition); blockPacket.setRuntimeId(blockId); - blockPacket.getFlags().add(UpdateBlockPacket.Flag.PRIORITY); + blockPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY); session.getUpstream().sendPacket(blockPacket); - tag = CompoundTag.EMPTY.toBuilder() + tag = CompoundTag.builder() .stringTag("id", "Chest") .intTag("x", pairPosition.getX()) .intTag("y", pairPosition.getY()) @@ -90,6 +97,16 @@ public class DoubleChestInventoryTranslator extends BlockInventoryTranslator { 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(); @@ -113,24 +130,11 @@ public class DoubleChestInventoryTranslator extends BlockInventoryTranslator { @Override public void updateInventory(GeyserSession session, Inventory inventory) { - //need to pad empty slots for 4x9 and 5x9 - ItemData[] bedrockItems = new ItemData[54]; - for (int i = 0; i < bedrockItems.length; i++) { - if (i <= this.size) { - bedrockItems[i] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(i)); - } else { - bedrockItems[i] = ItemData.AIR; - } - } - InventoryContentPacket contentPacket = new InventoryContentPacket(); - contentPacket.setContainerId(inventory.getId()); - contentPacket.setContents(bedrockItems); - session.getUpstream().sendPacket(contentPacket); + updater.updateInventory(this, session, inventory); + } - Inventory playerInventory = session.getInventory(); - for (int i = 0; i < 36; i++) { - playerInventory.setItem(i + 9, inventory.getItem(i + this.size)); - } - TranslatorsInit.getInventoryTranslators().get(playerInventory.getWindowType()).updateInventory(session, playerInventory); + @Override + public void updateSlot(GeyserSession session, Inventory inventory, int slot) { + updater.updateSlot(this, session, inventory, slot); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/EnchantmentInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/EnchantmentInventoryTranslator.java index 0b4ed4ba9..ba7f8cc7a 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/EnchantmentInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/EnchantmentInventoryTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2020 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 @@ -28,10 +28,11 @@ package org.geysermc.connector.network.translators.inventory; import com.nukkitx.protocol.bedrock.data.ContainerType; import org.geysermc.connector.inventory.Inventory; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.inventory.updater.ContainerInventoryUpdater; public class EnchantmentInventoryTranslator extends BlockInventoryTranslator { public EnchantmentInventoryTranslator() { - super(2, "minecraft:enchanting_table", ContainerType.ENCHANTMENT); + super(2, "minecraft:enchanting_table", ContainerType.ENCHANTMENT, new ContainerInventoryUpdater()); } @Override diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/FurnaceInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/FurnaceInventoryTranslator.java index a2eefac40..9b45201ed 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/FurnaceInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/FurnaceInventoryTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2020 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 @@ -30,10 +30,11 @@ import com.nukkitx.protocol.bedrock.data.ContainerType; import com.nukkitx.protocol.bedrock.packet.ContainerSetDataPacket; import org.geysermc.connector.inventory.Inventory; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.inventory.updater.ContainerInventoryUpdater; public class FurnaceInventoryTranslator extends BlockInventoryTranslator { public FurnaceInventoryTranslator() { - super(3, "minecraft:furnace[facing=north,lit=false]", ContainerType.FURNACE); + super(3, "minecraft:furnace[facing=north,lit=false]", ContainerType.FURNACE, new ContainerInventoryUpdater()); } @Override diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/InventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/InventoryTranslator.java index 915621940..7baef61a5 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/InventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/InventoryTranslator.java @@ -26,16 +26,16 @@ package org.geysermc.connector.network.translators.inventory; import com.nukkitx.protocol.bedrock.data.InventoryAction; +import lombok.AllArgsConstructor; import org.geysermc.connector.inventory.Inventory; import org.geysermc.connector.network.session.GeyserSession; +import java.util.List; + +@AllArgsConstructor public abstract class InventoryTranslator { public final int size; - InventoryTranslator(int size) { - this.size = size; - } - public abstract void prepareInventory(GeyserSession session, Inventory inventory); public abstract void openInventory(GeyserSession session, Inventory inventory); public abstract void closeInventory(GeyserSession session, Inventory inventory); @@ -45,4 +45,5 @@ public abstract class InventoryTranslator { public abstract int bedrockSlotToJava(InventoryAction action); public abstract int javaSlotToBedrock(int slot); public abstract SlotType getSlotType(int javaSlot); + public abstract void translateActions(GeyserSession session, Inventory inventory, List actions); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/PlayerInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/PlayerInventoryTranslator.java index 6ba25bc52..acad709f5 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/PlayerInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/PlayerInventoryTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2020 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 @@ -25,12 +25,19 @@ package org.geysermc.connector.network.translators.inventory; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; +import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; +import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientCreativeInventoryActionPacket; import com.nukkitx.protocol.bedrock.data.*; import com.nukkitx.protocol.bedrock.packet.InventoryContentPacket; import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket; 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.action.InventoryActionTranslator; +import org.geysermc.connector.utils.InventoryUtils; + +import java.util.List; public class PlayerInventoryTranslator extends InventoryTranslator { public PlayerInventoryTranslator() { @@ -39,20 +46,26 @@ public class PlayerInventoryTranslator extends InventoryTranslator { @Override public void updateInventory(GeyserSession session, Inventory inventory) { + // Crafting grid + for (int i = 1; i < 5; i++) { + InventorySlotPacket slotPacket = new InventorySlotPacket(); + slotPacket.setContainerId(ContainerId.CURSOR); + slotPacket.setInventorySlot(i + 27); + slotPacket.setSlot(TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(i))); + session.getUpstream().sendPacket(slotPacket); + } + InventoryContentPacket inventoryContentPacket = new InventoryContentPacket(); inventoryContentPacket.setContainerId(ContainerId.INVENTORY); - ItemData[] contents = new ItemData[36]; // Inventory for (int i = 9; i < 36; i++) { contents[i] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(i)); } - // Hotbar for (int i = 36; i < 45; i++) { contents[i - 36] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(i)); } - inventoryContentPacket.setContents(contents); session.getUpstream().sendPacket(inventoryContentPacket); @@ -75,7 +88,7 @@ public class PlayerInventoryTranslator extends InventoryTranslator { @Override public void updateSlot(GeyserSession session, Inventory inventory, int slot) { - if (slot >= 5 && slot <= 44) { + if (slot >= 1 && slot <= 44) { InventorySlotPacket slotPacket = new InventorySlotPacket(); if (slot >= 9) { slotPacket.setContainerId(ContainerId.INVENTORY); @@ -84,9 +97,12 @@ public class PlayerInventoryTranslator extends InventoryTranslator { } else { slotPacket.setInventorySlot(slot); } - } else { + } else if (slot >= 5) { slotPacket.setContainerId(ContainerId.ARMOR); slotPacket.setInventorySlot(slot - 5); + } else { + slotPacket.setContainerId(ContainerId.CURSOR); + slotPacket.setInventorySlot(slot + 27); } slotPacket.setSlot(TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(slot))); session.getUpstream().sendPacket(slotPacket); @@ -142,6 +158,55 @@ public class PlayerInventoryTranslator extends InventoryTranslator { return SlotType.NORMAL; } + @Override + public void translateActions(GeyserSession session, Inventory inventory, List actions) { + if (session.getGameMode() == GameMode.CREATIVE) { + //crafting grid is not visible in creative mode in java edition + for (InventoryAction action : actions) { + if (action.getSource().getContainerId() == ContainerId.CURSOR && (action.getSlot() >= 28 && 31 >= action.getSlot())) { + updateInventory(session, inventory); + InventoryUtils.updateCursor(session); + return; + } + } + + ItemStack javaItem; + for (InventoryAction action : actions) { + switch (action.getSource().getContainerId()) { + case ContainerId.INVENTORY: + case ContainerId.ARMOR: + case ContainerId.OFFHAND: + int javaSlot = bedrockSlotToJava(action); + if (action.getToItem().getId() == 0) { + javaItem = new ItemStack(-1, 0, null); + } else { + javaItem = TranslatorsInit.getItemTranslator().translateToJava(action.getToItem()); + } + ClientCreativeInventoryActionPacket creativePacket = new ClientCreativeInventoryActionPacket(javaSlot, InventoryUtils.fixStack(javaItem)); + session.getDownstream().getSession().send(creativePacket); + inventory.setItem(javaSlot, javaItem); + break; + case ContainerId.CURSOR: + if (action.getSlot() == 0) { + session.getInventory().setCursor(TranslatorsInit.getItemTranslator().translateToJava(action.getToItem())); + } + break; + case ContainerId.NONE: + if (action.getSource().getType() == InventorySource.Type.WORLD_INTERACTION + && action.getSource().getFlag() == InventorySource.Flag.DROP_ITEM) { + javaItem = TranslatorsInit.getItemTranslator().translateToJava(action.getToItem()); + ClientCreativeInventoryActionPacket creativeDropPacket = new ClientCreativeInventoryActionPacket(-1, InventoryUtils.fixStack(javaItem)); + session.getDownstream().getSession().send(creativeDropPacket); + } + break; + } + } + return; + } + + InventoryActionTranslator.translate(this, session, inventory, actions); + } + @Override public void prepareInventory(GeyserSession session, Inventory inventory) { } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/SingleChestInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/SingleChestInventoryTranslator.java index 84426eaa0..5c99b0126 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/SingleChestInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/SingleChestInventoryTranslator.java @@ -26,37 +26,10 @@ package org.geysermc.connector.network.translators.inventory; import com.nukkitx.protocol.bedrock.data.ContainerType; -import com.nukkitx.protocol.bedrock.data.ItemData; -import com.nukkitx.protocol.bedrock.packet.*; -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.updater.ChestInventoryUpdater; public class SingleChestInventoryTranslator extends BlockInventoryTranslator { public SingleChestInventoryTranslator(int size) { - super(size, "minecraft:chest[facing=north,type=single,waterlogged=false]", ContainerType.CONTAINER); - } - - @Override - public void updateInventory(GeyserSession session, Inventory inventory) { - //need to pad empty slots for 1x9 and 2x9 - ItemData[] bedrockItems = new ItemData[27]; - for (int i = 0; i < bedrockItems.length; i++) { - if (i <= this.size) { - bedrockItems[i] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(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.setItem(i + 9, inventory.getItem(i + this.size)); - } - TranslatorsInit.getInventoryTranslators().get(playerInventory.getWindowType()).updateInventory(session, playerInventory); + super(size, "minecraft:chest[facing=north,type=single,waterlogged=false]", ContainerType.CONTAINER, new ChestInventoryUpdater(27)); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/SlotType.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/SlotType.java index b5657af46..045adbd32 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/SlotType.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/SlotType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2020 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 diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/action/Click.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/action/Click.java new file mode 100644 index 000000000..1fdfa3640 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/action/Click.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2019-2020 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.action; + +import com.github.steveice10.mc.protocol.data.game.window.ClickItemParam; +import com.github.steveice10.mc.protocol.data.game.window.WindowActionParam; +import lombok.AllArgsConstructor; + +@AllArgsConstructor +enum Click { + LEFT(ClickItemParam.LEFT_CLICK), + RIGHT(ClickItemParam.RIGHT_CLICK); + + public final WindowActionParam actionParam; +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/action/ClickPlan.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/action/ClickPlan.java new file mode 100644 index 000000000..3abdd2843 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/action/ClickPlan.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2019-2020 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.action; + +import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; +import com.github.steveice10.mc.protocol.data.game.window.WindowAction; +import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientConfirmTransactionPacket; +import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientWindowActionPacket; +import org.geysermc.connector.inventory.Inventory; +import org.geysermc.connector.inventory.PlayerInventory; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.inventory.InventoryTranslator; +import org.geysermc.connector.network.translators.inventory.SlotType; +import org.geysermc.connector.utils.InventoryUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.ListIterator; + +class ClickPlan { + private final List plan = new ArrayList<>(); + + public void add(Click click, int slot) { + plan.add(new ClickAction(click, slot)); + } + + public void execute(GeyserSession session, InventoryTranslator translator, Inventory inventory, boolean refresh) { + PlayerInventory playerInventory = session.getInventory(); + ListIterator planIter = plan.listIterator(); + while (planIter.hasNext()) { + final ClickAction action = planIter.next(); + final ItemStack cursorItem = playerInventory.getCursor(); + final ItemStack clickedItem = inventory.getItem(action.slot); + final short actionId = (short) inventory.getTransactionId().getAndIncrement(); + + //TODO: stop relying on refreshing the inventory for crafting to work properly + if (translator.getSlotType(action.slot) != SlotType.NORMAL) + refresh = true; + + ClientWindowActionPacket clickPacket = new ClientWindowActionPacket(inventory.getId(), + actionId, action.slot, !planIter.hasNext() && refresh ? InventoryUtils.REFRESH_ITEM : InventoryUtils.fixStack(clickedItem), + WindowAction.CLICK_ITEM, action.click.actionParam); + + if (translator.getSlotType(action.slot) == SlotType.OUTPUT) { + if (cursorItem == null && clickedItem != null) { + playerInventory.setCursor(clickedItem); + } else if (InventoryUtils.canStack(cursorItem, clickedItem)) { + playerInventory.setCursor(new ItemStack(cursorItem.getId(), + cursorItem.getAmount() + clickedItem.getAmount(), cursorItem.getNbt())); + } + } else { + switch (action.click) { + case LEFT: + if (!InventoryUtils.canStack(cursorItem, clickedItem)) { + playerInventory.setCursor(clickedItem); + inventory.setItem(action.slot, cursorItem); + } else { + playerInventory.setCursor(null); + inventory.setItem(action.slot, new ItemStack(clickedItem.getId(), + clickedItem.getAmount() + cursorItem.getAmount(), clickedItem.getNbt())); + } + break; + case RIGHT: + if (cursorItem == null && clickedItem != null) { + ItemStack halfItem = new ItemStack(clickedItem.getId(), + clickedItem.getAmount() / 2, clickedItem.getNbt()); + inventory.setItem(action.slot, halfItem); + playerInventory.setCursor(new ItemStack(clickedItem.getId(), + clickedItem.getAmount() - halfItem.getAmount(), clickedItem.getNbt())); + } else if (cursorItem != null && clickedItem == null) { + playerInventory.setCursor(new ItemStack(cursorItem.getId(), + cursorItem.getAmount() - 1, cursorItem.getNbt())); + inventory.setItem(action.slot, new ItemStack(cursorItem.getId(), + 1, cursorItem.getNbt())); + } else if (InventoryUtils.canStack(cursorItem, clickedItem)) { + playerInventory.setCursor(new ItemStack(cursorItem.getId(), + cursorItem.getAmount() - 1, cursorItem.getNbt())); + inventory.setItem(action.slot, new ItemStack(clickedItem.getId(), + clickedItem.getAmount() + 1, clickedItem.getNbt())); + } + break; + } + } + session.getDownstream().getSession().send(clickPacket); + session.getDownstream().getSession().send(new ClientConfirmTransactionPacket(inventory.getId(), actionId, true)); + } + + /*if (refresh) { + translator.updateInventory(session, inventory); + InventoryUtils.updateCursor(session); + }*/ + } + + private static class ClickAction { + final Click click; + final int slot; + ClickAction(Click click, int slot) { + this.click = click; + this.slot = slot; + } + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/action/InventoryActionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/action/InventoryActionTranslator.java new file mode 100644 index 000000000..586c2c023 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/action/InventoryActionTranslator.java @@ -0,0 +1,330 @@ +/* + * Copyright (c) 2019-2020 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.action; + +import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; +import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerAction; +import com.github.steveice10.mc.protocol.data.game.window.*; +import com.github.steveice10.mc.protocol.data.game.world.block.BlockFace; +import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerActionPacket; +import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientWindowActionPacket; +import com.nukkitx.protocol.bedrock.data.ContainerId; +import com.nukkitx.protocol.bedrock.data.InventoryAction; +import com.nukkitx.protocol.bedrock.data.InventorySource; +import com.nukkitx.protocol.bedrock.data.ItemData; +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.InventoryTranslator; +import org.geysermc.connector.network.translators.inventory.SlotType; +import org.geysermc.connector.utils.InventoryUtils; + +import java.util.*; + +public class InventoryActionTranslator { + public static void translate(InventoryTranslator translator, GeyserSession session, Inventory inventory, List actions) { + if (actions.size() != 2) + return; + + InventoryAction worldAction = null; + InventoryAction cursorAction = null; + InventoryAction containerAction = null; + boolean refresh = false; + for (InventoryAction action : actions) { + if (action.getSource().getContainerId() == ContainerId.CRAFTING_USE_INGREDIENT || action.getSource().getContainerId() == ContainerId.CRAFTING_RESULT) { + return; + } else if (action.getSource().getType() == InventorySource.Type.WORLD_INTERACTION) { + worldAction = action; + } else if (action.getSource().getContainerId() == ContainerId.CURSOR && action.getSlot() == 0) { + cursorAction = action; + ItemData translatedCursor = TranslatorsInit.getItemTranslator().translateToBedrock(session.getInventory().getCursor()); + if (!translatedCursor.equals(action.getFromItem())) { + refresh = true; + } + } else { + containerAction = action; + ItemData translatedItem = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(translator.bedrockSlotToJava(action))); + if (!translatedItem.equals(action.getFromItem())) { + refresh = true; + } + } + } + + final int craftSlot = session.getCraftSlot(); + session.setCraftSlot(0); + + if (worldAction != null) { + InventoryAction sourceAction; + if (cursorAction != null) { + sourceAction = cursorAction; + } else { + sourceAction = containerAction; + } + + if (sourceAction != null) { + if (worldAction.getSource().getFlag() == InventorySource.Flag.DROP_ITEM) { + //quick dropping from hotbar? + if (session.getInventoryCache().getOpenInventory() == null && sourceAction.getSource().getContainerId() == ContainerId.INVENTORY) { + int heldSlot = session.getInventory().getHeldItemSlot(); + if (sourceAction.getSlot() == heldSlot) { + ClientPlayerActionPacket actionPacket = new ClientPlayerActionPacket( + sourceAction.getToItem().getCount() == 0 ? PlayerAction.DROP_ITEM_STACK : PlayerAction.DROP_ITEM, + new Position(0, 0, 0), BlockFace.DOWN); + session.getDownstream().getSession().send(actionPacket); + ItemStack item = session.getInventory().getItem(heldSlot); + if (item != null) { + session.getInventory().setItem(heldSlot, new ItemStack(item.getId(), item.getAmount() - 1, item.getNbt())); + } + return; + } + } + int dropAmount = sourceAction.getFromItem().getCount() - sourceAction.getToItem().getCount(); + if (sourceAction != cursorAction) { //dropping directly from inventory + int javaSlot = translator.bedrockSlotToJava(sourceAction); + if (dropAmount == sourceAction.getFromItem().getCount()) { + ClientWindowActionPacket dropPacket = new ClientWindowActionPacket(inventory.getId(), + inventory.getTransactionId().getAndIncrement(), + javaSlot, null, WindowAction.DROP_ITEM, + DropItemParam.DROP_SELECTED_STACK); + session.getDownstream().getSession().send(dropPacket); + } else { + for (int i = 0; i < dropAmount; i++) { + ClientWindowActionPacket dropPacket = new ClientWindowActionPacket(inventory.getId(), + inventory.getTransactionId().getAndIncrement(), + javaSlot, null, WindowAction.DROP_ITEM, + DropItemParam.DROP_FROM_SELECTED); + session.getDownstream().getSession().send(dropPacket); + } + } + ItemStack item = session.getInventory().getItem(javaSlot); + if (item != null) { + session.getInventory().setItem(javaSlot, new ItemStack(item.getId(), item.getAmount() - dropAmount, item.getNbt())); + } + return; + } else { //clicking outside of inventory + ClientWindowActionPacket dropPacket = new ClientWindowActionPacket(inventory.getId(), inventory.getTransactionId().getAndIncrement(), + -999, null, WindowAction.CLICK_ITEM, + dropAmount > 1 ? ClickItemParam.LEFT_CLICK : ClickItemParam.RIGHT_CLICK); + session.getDownstream().getSession().send(dropPacket); + ItemStack cursor = session.getInventory().getCursor(); + if (cursor != null) { + session.getInventory().setCursor(new ItemStack(cursor.getId(), dropAmount > 1 ? 0 : cursor.getAmount() - 1, cursor.getNbt())); + } + return; + } + } + } + } else if (cursorAction != null && containerAction != null) { + //left/right click + ClickPlan plan = new ClickPlan(); + int javaSlot = translator.bedrockSlotToJava(containerAction); + if (cursorAction.getFromItem().equals(containerAction.getToItem()) + && containerAction.getFromItem().equals(cursorAction.getToItem()) + && !InventoryUtils.canStack(cursorAction.getFromItem(), containerAction.getFromItem())) { //simple swap + plan.add(Click.LEFT, javaSlot); + } else if (cursorAction.getFromItem().getCount() > cursorAction.getToItem().getCount()) { //release + if (cursorAction.getToItem().getCount() == 0) { + plan.add(Click.LEFT, javaSlot); + } else { + int difference = cursorAction.getFromItem().getCount() - cursorAction.getToItem().getCount(); + for (int i = 0; i < difference; i++) { + plan.add(Click.RIGHT, javaSlot); + } + } + } else { //pickup + if (cursorAction.getFromItem().getCount() == 0) { + if (containerAction.getToItem().getCount() == 0) { //pickup all + plan.add(Click.LEFT, javaSlot); + } else { //pickup some + if (translator.getSlotType(javaSlot) == SlotType.FURNACE_OUTPUT + || containerAction.getToItem().getCount() == containerAction.getFromItem().getCount() / 2) { //right click + plan.add(Click.RIGHT, javaSlot); + } else { + plan.add(Click.LEFT, javaSlot); + int difference = containerAction.getFromItem().getCount() - cursorAction.getToItem().getCount(); + for (int i = 0; i < difference; i++) { + plan.add(Click.RIGHT, javaSlot); + } + } + } + } else { //pickup into non-empty cursor + if (translator.getSlotType(javaSlot) == SlotType.FURNACE_OUTPUT) { + if (containerAction.getToItem().getCount() == 0) { + plan.add(Click.LEFT, javaSlot); + } else { + ClientWindowActionPacket shiftClickPacket = new ClientWindowActionPacket(inventory.getId(), + inventory.getTransactionId().getAndIncrement(), + javaSlot, InventoryUtils.REFRESH_ITEM, WindowAction.SHIFT_CLICK_ITEM, + ShiftClickItemParam.LEFT_CLICK); + session.getDownstream().getSession().send(shiftClickPacket); + translator.updateInventory(session, inventory); + return; + } + } else if (translator.getSlotType(javaSlot) == SlotType.OUTPUT) { + plan.add(Click.LEFT, javaSlot); + } else { + int cursorSlot = findTempSlot(inventory, session.getInventory().getCursor(), Collections.singletonList(javaSlot)); + if (cursorSlot != -1) { + plan.add(Click.LEFT, cursorSlot); + } else { + translator.updateInventory(session, inventory); + return; + } + plan.add(Click.LEFT, javaSlot); + int difference = cursorAction.getToItem().getCount() - cursorAction.getFromItem().getCount(); + for (int i = 0; i < difference; i++) { + plan.add(Click.RIGHT, cursorSlot); + } + plan.add(Click.LEFT, javaSlot); + plan.add(Click.LEFT, cursorSlot); + } + } + } + plan.execute(session, translator, inventory, refresh); + return; + } else { + ClickPlan plan = new ClickPlan(); + InventoryAction fromAction; + InventoryAction toAction; + if (actions.get(0).getFromItem().getCount() >= actions.get(0).getToItem().getCount()) { + fromAction = actions.get(0); + toAction = actions.get(1); + } else { + fromAction = actions.get(1); + toAction = actions.get(0); + } + int fromSlot = translator.bedrockSlotToJava(fromAction); + int toSlot = translator.bedrockSlotToJava(toAction); + + if (translator.getSlotType(fromSlot) == SlotType.OUTPUT) { + if ((craftSlot != 0 && craftSlot != -2) && (inventory.getItem(toSlot) == null + || InventoryUtils.canStack(session.getInventory().getCursor(), inventory.getItem(toSlot)))) { + if (fromAction.getToItem().getCount() == 0) { + refresh = true; + plan.add(Click.LEFT, toSlot); + if (craftSlot != -1) { + plan.add(Click.LEFT, craftSlot); + } + } else { + int difference = toAction.getToItem().getCount() - toAction.getFromItem().getCount(); + for (int i = 0; i < difference; i++) { + plan.add(Click.RIGHT, toSlot); + } + session.setCraftSlot(craftSlot); + } + plan.execute(session, translator, inventory, refresh); + return; + } else { + session.setCraftSlot(-2); + } + } + + int cursorSlot = -1; + if (session.getInventory().getCursor() != null) { //move cursor contents to a temporary slot + cursorSlot = findTempSlot(inventory, session.getInventory().getCursor(), Arrays.asList(fromSlot, toSlot)); + if (cursorSlot != -1) { + plan.add(Click.LEFT, cursorSlot); + } else { + translator.updateInventory(session, inventory); + return; + } + } + if ((fromAction.getFromItem().equals(toAction.getToItem()) && !InventoryUtils.canStack(fromAction.getFromItem(), toAction.getFromItem())) + || fromAction.getToItem().getId() == 0) { //slot swap + plan.add(Click.LEFT, fromSlot); + plan.add(Click.LEFT, toSlot); + if (fromAction.getToItem().getId() != 0) { + plan.add(Click.LEFT, fromSlot); + } + } else if (InventoryUtils.canStack(fromAction.getFromItem(), toAction.getToItem())) { //partial item move + if (translator.getSlotType(fromSlot) == SlotType.FURNACE_OUTPUT) { + ClientWindowActionPacket shiftClickPacket = new ClientWindowActionPacket(inventory.getId(), + inventory.getTransactionId().getAndIncrement(), + fromSlot, InventoryUtils.REFRESH_ITEM, WindowAction.SHIFT_CLICK_ITEM, + ShiftClickItemParam.LEFT_CLICK); + session.getDownstream().getSession().send(shiftClickPacket); + translator.updateInventory(session, inventory); + return; + } else if (translator.getSlotType(fromSlot) == SlotType.OUTPUT) { + session.setCraftSlot(cursorSlot); + plan.add(Click.LEFT, fromSlot); + int difference = toAction.getToItem().getCount() - toAction.getFromItem().getCount(); + for (int i = 0; i < difference; i++) { + plan.add(Click.RIGHT, toSlot); + } + //client will send additional packets later to finish transferring crafting output + //translator will know how to handle this using the craftSlot variable + } else { + plan.add(Click.LEFT, fromSlot); + int difference = toAction.getToItem().getCount() - toAction.getFromItem().getCount(); + for (int i = 0; i < difference; i++) { + plan.add(Click.RIGHT, toSlot); + } + plan.add(Click.LEFT, fromSlot); + } + } + if (cursorSlot != -1) { + plan.add(Click.LEFT, cursorSlot); + } + plan.execute(session, translator, inventory, refresh); + return; + } + + translator.updateInventory(session, inventory); + InventoryUtils.updateCursor(session); + } + + private static int findTempSlot(Inventory inventory, ItemStack item, List slotBlacklist) { + /*try and find a slot that can temporarily store the given item + only look in the main inventory and hotbar + only slots that are empty or contain a different type of item are valid*/ + int offset = inventory.getId() == 0 ? 1 : 0; //offhand is not a viable slot (some servers disable it) + List itemBlacklist = new ArrayList<>(slotBlacklist.size() + 1); + itemBlacklist.add(item); + for (int slot : slotBlacklist) { + ItemStack blacklistItem = inventory.getItem(slot); + if (blacklistItem != null) + itemBlacklist.add(blacklistItem); + } + for (int i = inventory.getSize() - (36 + offset); i < inventory.getSize() - offset; i++) { + ItemStack testItem = inventory.getItem(i); + boolean acceptable = true; + if (testItem != null) { + for (ItemStack blacklistItem : itemBlacklist) { + if (InventoryUtils.canStack(testItem, blacklistItem)) { + acceptable = false; + break; + } + } + } + if (acceptable && !slotBlacklist.contains(i)) + return i; + } + //could not find a viable temp slot + return -1; + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/holder/BlockInventoryHolder.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/holder/BlockInventoryHolder.java new file mode 100644 index 000000000..6c0db853d --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/holder/BlockInventoryHolder.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2019-2020 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.holder; + +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.packet.BlockEntityDataPacket; +import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket; +import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket; +import lombok.AllArgsConstructor; +import org.geysermc.connector.inventory.Inventory; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.block.BlockEntry; +import org.geysermc.connector.network.translators.inventory.InventoryTranslator; + +@AllArgsConstructor +public class BlockInventoryHolder extends InventoryHolder { + private final int blockId; + private final ContainerType containerType; + + @Override + public void prepareInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) { + Vector3i position = session.getPlayerEntity().getPosition().toInt(); + position = position.add(Vector3i.UP); + UpdateBlockPacket blockPacket = new UpdateBlockPacket(); + blockPacket.setDataLayer(0); + blockPacket.setBlockPosition(position); + blockPacket.setRuntimeId(blockId); + blockPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY); + session.getUpstream().sendPacket(blockPacket); + inventory.setHolderPosition(position); + + CompoundTag tag = CompoundTag.builder() + .intTag("x", position.getX()) + .intTag("y", position.getY()) + .intTag("z", position.getZ()) + .stringTag("CustomName", inventory.getTitle()).buildRootTag(); + BlockEntityDataPacket dataPacket = new BlockEntityDataPacket(); + dataPacket.setData(tag); + dataPacket.setBlockPosition(position); + session.getUpstream().sendPacket(dataPacket); + } + + @Override + public void openInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) { + ContainerOpenPacket containerOpenPacket = new ContainerOpenPacket(); + containerOpenPacket.setWindowId((byte) inventory.getId()); + containerOpenPacket.setType((byte) containerType.id()); + containerOpenPacket.setBlockPosition(inventory.getHolderPosition()); + containerOpenPacket.setUniqueEntityId(inventory.getHolderId()); + session.getUpstream().sendPacket(containerOpenPacket); + } + + @Override + public void closeInventory(InventoryTranslator translator, 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(realBlock.getBedrockRuntimeId()); + session.getUpstream().sendPacket(blockPacket); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/holder/InventoryHolder.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/holder/InventoryHolder.java new file mode 100644 index 000000000..5a9e736e9 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/holder/InventoryHolder.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2019-2020 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.holder; + +import org.geysermc.connector.inventory.Inventory; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.inventory.InventoryTranslator; + +public abstract class InventoryHolder { + public abstract void prepareInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory); + public abstract void openInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory); + public abstract void closeInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory); +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/ChestInventoryUpdater.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/ChestInventoryUpdater.java new file mode 100644 index 000000000..4af1fba13 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/ChestInventoryUpdater.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2019-2020 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.updater; + +import com.nukkitx.protocol.bedrock.data.ItemData; +import com.nukkitx.protocol.bedrock.packet.InventoryContentPacket; +import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket; +import lombok.AllArgsConstructor; +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.InventoryTranslator; + +@AllArgsConstructor +public class ChestInventoryUpdater extends InventoryUpdater { + private final int paddedSize; + + @Override + public void updateInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) { + super.updateInventory(translator, session, inventory); + + ItemData[] bedrockItems = new ItemData[paddedSize]; + for (int i = 0; i < bedrockItems.length; i++) { + if (i <= translator.size) { + bedrockItems[i] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(i)); + } else { + bedrockItems[i] = ItemData.AIR; + } + } + + InventoryContentPacket contentPacket = new InventoryContentPacket(); + contentPacket.setContainerId(inventory.getId()); + contentPacket.setContents(bedrockItems); + session.getUpstream().sendPacket(contentPacket); + } + + @Override + public boolean updateSlot(InventoryTranslator translator, GeyserSession session, Inventory inventory, int javaSlot) { + if (super.updateSlot(translator, session, inventory, javaSlot)) + return true; + + InventorySlotPacket slotPacket = new InventorySlotPacket(); + slotPacket.setContainerId(inventory.getId()); + slotPacket.setInventorySlot(translator.javaSlotToBedrock(javaSlot)); + slotPacket.setSlot(TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(javaSlot))); + session.getUpstream().sendPacket(slotPacket); + return true; + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/ContainerInventoryUpdater.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/ContainerInventoryUpdater.java new file mode 100644 index 000000000..7169311ed --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/ContainerInventoryUpdater.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2019-2020 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.updater; + +import com.nukkitx.protocol.bedrock.data.ItemData; +import com.nukkitx.protocol.bedrock.packet.InventoryContentPacket; +import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket; +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.InventoryTranslator; + +public class ContainerInventoryUpdater extends InventoryUpdater { + @Override + public void updateInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) { + super.updateInventory(translator, session, inventory); + + ItemData[] bedrockItems = new ItemData[translator.size]; + for (int i = 0; i < bedrockItems.length; i++) { + bedrockItems[translator.javaSlotToBedrock(i)] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(i)); + } + + InventoryContentPacket contentPacket = new InventoryContentPacket(); + contentPacket.setContainerId(inventory.getId()); + contentPacket.setContents(bedrockItems); + session.getUpstream().sendPacket(contentPacket); + } + + @Override + public boolean updateSlot(InventoryTranslator translator, GeyserSession session, Inventory inventory, int javaSlot) { + if (super.updateSlot(translator, session, inventory, javaSlot)) + return true; + + InventorySlotPacket slotPacket = new InventorySlotPacket(); + slotPacket.setContainerId(inventory.getId()); + slotPacket.setInventorySlot(translator.javaSlotToBedrock(javaSlot)); + slotPacket.setSlot(TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(javaSlot))); + session.getUpstream().sendPacket(slotPacket); + return true; + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/CursorInventoryUpdater.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/CursorInventoryUpdater.java new file mode 100644 index 000000000..3df8d7662 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/CursorInventoryUpdater.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2019-2020 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.updater; + +import com.nukkitx.protocol.bedrock.data.ContainerId; +import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket; +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.InventoryTranslator; + +public class CursorInventoryUpdater extends InventoryUpdater { + @Override + public void updateInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) { + super.updateInventory(translator, session, inventory); + + for (int i = 0; i < translator.size; i++) { + final int bedrockSlot = translator.javaSlotToBedrock(i); + if (bedrockSlot == 50) + continue; + InventorySlotPacket slotPacket = new InventorySlotPacket(); + slotPacket.setContainerId(ContainerId.CURSOR); + slotPacket.setInventorySlot(bedrockSlot); + slotPacket.setSlot(TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(i))); + session.getUpstream().sendPacket(slotPacket); + } + } + + @Override + public boolean updateSlot(InventoryTranslator translator, GeyserSession session, Inventory inventory, int javaSlot) { + if (super.updateSlot(translator, session, inventory, javaSlot)) + return true; + + InventorySlotPacket slotPacket = new InventorySlotPacket(); + slotPacket.setContainerId(ContainerId.CURSOR); + slotPacket.setInventorySlot(translator.javaSlotToBedrock(javaSlot)); + slotPacket.setSlot(TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(javaSlot))); + session.getUpstream().sendPacket(slotPacket); + return true; + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/InventoryUpdater.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/InventoryUpdater.java new file mode 100644 index 000000000..e5b6f4c56 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/InventoryUpdater.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2019-2020 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.updater; + +import com.nukkitx.protocol.bedrock.data.ContainerId; +import com.nukkitx.protocol.bedrock.data.ItemData; +import com.nukkitx.protocol.bedrock.packet.InventoryContentPacket; +import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket; +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.InventoryTranslator; + +public abstract class InventoryUpdater { + public void updateInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) { + ItemData[] bedrockItems = new ItemData[36]; + for (int i = 0; i < 36; i++) { + final int offset = i < 9 ? 27 : -9; + bedrockItems[i] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(translator.size + i + offset)); + } + InventoryContentPacket contentPacket = new InventoryContentPacket(); + contentPacket.setContainerId(ContainerId.INVENTORY); + contentPacket.setContents(bedrockItems); + session.getUpstream().sendPacket(contentPacket); + } + + public boolean updateSlot(InventoryTranslator translator, GeyserSession session, Inventory inventory, int javaSlot) { + if (javaSlot >= translator.size) { + InventorySlotPacket slotPacket = new InventorySlotPacket(); + slotPacket.setContainerId(ContainerId.INVENTORY); + slotPacket.setInventorySlot(translator.javaSlotToBedrock(javaSlot)); + slotPacket.setSlot(TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItem(javaSlot))); + session.getUpstream().sendPacket(slotPacket); + return true; + } + return false; + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/Enchantment.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/Enchantment.java index 596ef2153..c5c152a2f 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/Enchantment.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/Enchantment.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2020 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 diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java index ed61e4912..aaf00169d 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java @@ -74,9 +74,8 @@ public class ItemTranslator { } public ItemData translateToBedrock(ItemStack stack) { - // Most likely dirt if null if (stack == null) { - return ItemData.of(3, (short)0, 0); + return ItemData.AIR; } ItemEntry bedrockItem = getItem(stack); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/Potion.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/Potion.java index ca5cd5c0f..f711d3ea2 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/Potion.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/Potion.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2020 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 diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareRecipesTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareRecipesTranslator.java index 9015fc28f..9399d1dd1 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareRecipesTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareRecipesTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2020 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 @@ -49,8 +49,10 @@ import java.util.*; import java.util.stream.Collectors; public class JavaDeclareRecipesTranslator extends PacketTranslator { - - private final int[] brewingIngredients = new int[]{372, 331, 348, 376, 289, 437, 353, 414, 382, 375, 462, 378, 396, 377, 370, 469, 470}; + private static final Collection POTION_MIXES = + Arrays.stream(new int[]{372, 331, 348, 376, 289, 437, 353, 414, 382, 375, 462, 378, 396, 377, 370, 469, 470}) + .mapToObj(ingredient -> new PotionMixData(0, ingredient, 0)) + .collect(Collectors.toList()); @Override public void translate(ServerDeclareRecipesPacket packet, GeyserSession session) { @@ -61,6 +63,7 @@ public class JavaDeclareRecipesTranslator extends PacketTranslator, IntSet> squashedOptions = new HashMap<>(); for (int i = 0; i < ingredients.length; i++) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerChangeHeldItemTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerChangeHeldItemTranslator.java new file mode 100644 index 000000000..90042e32f --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerChangeHeldItemTranslator.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2019-2020 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.java.entity.player; + +import com.github.steveice10.mc.protocol.packet.ingame.server.entity.player.ServerPlayerChangeHeldItemPacket; +import com.nukkitx.protocol.bedrock.packet.PlayerHotbarPacket; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.PacketTranslator; + +public class JavaPlayerChangeHeldItemTranslator extends PacketTranslator { + + @Override + public void translate(ServerPlayerChangeHeldItemPacket packet, GeyserSession session) { + PlayerHotbarPacket hotbarPacket = new PlayerHotbarPacket(); + hotbarPacket.setContainerId(0); + hotbarPacket.setSelectedHotbarSlot(packet.getSlot()); + hotbarPacket.setSelectHotbarSlot(true); + session.getUpstream().sendPacket(hotbarPacket); + + session.getInventory().setHeldItemSlot(packet.getSlot()); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaCloseWindowTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaCloseWindowTranslator.java index dcb0c9feb..cb532e608 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaCloseWindowTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaCloseWindowTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2020 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 diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaConfirmTransactionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaConfirmTransactionTranslator.java index 09718dbd5..10c85de4a 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaConfirmTransactionTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaConfirmTransactionTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2020 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 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 43f3b6d15..4190ac5a4 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 @@ -80,7 +80,7 @@ public class JavaOpenWindowTranslator extends PacketTranslator InventoryUtils.openInventory(session, newInventory), 350, TimeUnit.MILLISECONDS); + Geyser.getGeneralThreadPool().schedule(() -> InventoryUtils.openInventory(session, newInventory), 500, TimeUnit.MILLISECONDS); return; } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaSetSlotTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaSetSlotTranslator.java index 9443f86b4..21c7217ae 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaSetSlotTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaSetSlotTranslator.java @@ -31,6 +31,7 @@ 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.Objects; @@ -43,8 +44,9 @@ public class JavaSetSlotTranslator extends PacketTranslator return; if (session.getCraftSlot() != 0) return; - //bedrock client is bugged when changing the cursor. do not send slot update packet + session.getInventory().setCursor(packet.getItem()); + InventoryUtils.updateCursor(session); return; } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaWindowPropertyTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaWindowPropertyTranslator.java index c8e04d377..d7e2292e1 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaWindowPropertyTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaWindowPropertyTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2020 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 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 60779f521..f55f28bbd 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java @@ -25,6 +25,11 @@ package org.geysermc.connector.utils; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; +import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import com.nukkitx.protocol.bedrock.data.ContainerId; +import com.nukkitx.protocol.bedrock.data.ItemData; +import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket; import org.geysermc.api.Geyser; import org.geysermc.connector.inventory.Inventory; import org.geysermc.connector.network.session.GeyserSession; @@ -32,9 +37,11 @@ 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; import java.util.concurrent.TimeUnit; public class InventoryUtils { + public static final ItemStack REFRESH_ITEM = new ItemStack(1, 127, new CompoundTag("")); //TODO: stop using this public static void openInventory(GeyserSession session, Inventory inventory) { InventoryTranslator translator = TranslatorsInit.getInventoryTranslators().get(inventory.getWindowType()); @@ -71,4 +78,31 @@ public class InventoryUtils { session.setCraftSlot(0); session.getInventory().setCursor(null); } + + public static void updateCursor(GeyserSession session) { + InventorySlotPacket cursorPacket = new InventorySlotPacket(); + cursorPacket.setContainerId(ContainerId.CURSOR); + cursorPacket.setInventorySlot(0); + cursorPacket.setSlot(TranslatorsInit.getItemTranslator().translateToBedrock(session.getInventory().getCursor())); + session.getUpstream().sendPacket(cursorPacket); + } + + //NPE if compound tag is null + public static ItemStack fixStack(ItemStack stack) { + if (stack == null || stack.getId() == 0) + return null; + return new ItemStack(stack.getId(), stack.getAmount(), stack.getNbt() == null ? new CompoundTag("") : stack.getNbt()); + } + + public static boolean canStack(ItemStack item1, ItemStack item2) { + if (item1 == null || item2 == null) + return false; + return item1.getId() == item2.getId() && Objects.equals(item1.getNbt(), item2.getNbt()); + } + + public static boolean canStack(ItemData item1, ItemData item2) { + if (item1 == null || item2 == null) + return false; + return item1.equals(item2, false, true, true); + } } diff --git a/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java b/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java index cc2f03b50..00bc86c29 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java +++ b/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java @@ -25,6 +25,7 @@ package org.geysermc.connector.utils; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.nukkitx.nbt.NbtUtils; import com.nukkitx.nbt.stream.NBTInputStream; @@ -150,31 +151,31 @@ public class Toolbox { InputStream creativeItemStream = Toolbox.class.getClassLoader().getResourceAsStream("bedrock/creative_items.json"); ObjectMapper creativeItemMapper = new ObjectMapper(); - List> creativeItemEntries = new ArrayList<>(); + JsonNode creativeItemEntries; try { - creativeItemEntries = (ArrayList>) creativeItemMapper.readValue(creativeItemStream, HashMap.class).get("items"); + creativeItemEntries = creativeItemMapper.readTree(creativeItemStream).get("items"); } catch (Exception e) { - e.printStackTrace(); + throw new AssertionError("Unable to load creative items", e); } List creativeItems = new ArrayList<>(); - for (Map map : creativeItemEntries) { + for (JsonNode itemNode : creativeItemEntries) { short damage = 0; - if (map.containsKey("damage")) { - damage = (short)(int) map.get("damage"); + if (itemNode.has("damage")) { + damage = itemNode.get("damage").numberValue().shortValue(); } - if (map.containsKey("nbt_b64")) { - byte[] bytes = Base64.getDecoder().decode((String) map.get("nbt_b64")); + if (itemNode.has("nbt_b64")) { + byte[] bytes = Base64.getDecoder().decode(itemNode.get("nbt_b64").asText()); ByteArrayInputStream bais = new ByteArrayInputStream(bytes); try { com.nukkitx.nbt.tag.CompoundTag tag = (com.nukkitx.nbt.tag.CompoundTag) NbtUtils.createReaderLE(bais).readTag(); - creativeItems.add(ItemData.of((int) map.get("id"), damage, 1, tag)); + creativeItems.add(ItemData.of(itemNode.get("id").asInt(), damage, 1, tag)); } catch (IOException e) { e.printStackTrace(); } } else { - creativeItems.add(ItemData.of((int) map.get("id"), damage, 1)); + creativeItems.add(ItemData.of(itemNode.get("id").asInt(), damage, 1)); } }