From 7f4b588cdfe16df614fc1ce5f6b723589965809e Mon Sep 17 00:00:00 2001 From: AJ Ferguson Date: Fri, 16 Oct 2020 15:25:05 -0800 Subject: [PATCH 01/39] server inventory. WORK IN PROGRESS wip commit of implementing server authoritative inventories. there is a lot of experimental and debug code. this is NOT ready for testing or review. --- .../org/geysermc/connector/entity/Entity.java | 8 +- .../connector/inventory/Container.java | 69 +++ .../inventory/EnchantmentInventory.java | 40 ++ .../connector/inventory/FurnaceInventory.java | 41 ++ .../connector/inventory/GeyserItemStack.java | 123 +++++ .../connector/inventory/Inventory.java | 30 +- .../inventory/MerchantContainer.java | 44 ++ .../connector/inventory/PlayerInventory.java | 22 +- .../network/session/GeyserSession.java | 81 ++- .../network/session/cache/InventoryCache.java | 61 --- .../BedrockContainerCloseTranslator.java | 32 +- ...BedrockInventoryTransactionTranslator.java | 45 +- .../BedrockItemStackRequestTranslator.java | 49 ++ .../BedrockMobEquipmentTranslator.java | 4 +- .../entity/BedrockEntityEventTranslator.java | 32 +- .../player/BedrockInteractTranslator.java | 7 +- .../inventory/AnvilInventoryTranslator.java | 167 ------- .../inventory/BaseInventoryTranslator.java | 50 +- .../inventory/BedrockContainerSlot.java | 36 ++ .../inventory/BlockInventoryTranslator.java | 72 --- .../inventory/BrewingInventoryTranslator.java | 99 ---- .../inventory/ChestInventoryTranslator.java | 20 +- .../CraftingInventoryTranslator.java | 89 ---- .../EnchantmentInventoryTranslator.java | 267 ---------- .../inventory/FurnaceInventoryTranslator.java | 70 --- .../GrindstoneInventoryTranslator.java | 69 --- .../inventory/InventoryTranslator.java | 460 +++++++++++++++++- .../MerchantInventoryTranslator.java | 92 ++-- .../inventory/PlayerInventoryTranslator.java | 115 ++--- .../SmithingInventoryTranslator.java | 69 --- .../inventory/action/ClickPlan.java | 125 ----- .../action/InventoryActionDataTranslator.java | 338 ------------- .../inventory/{action => click}/Click.java | 17 +- .../inventory/click/ClickPlan.java | 211 ++++++++ .../updater/ChestInventoryUpdater.java | 4 +- .../updater/ContainerInventoryUpdater.java | 4 +- .../inventory/updater/InventoryUpdater.java | 4 +- ...ryUpdater.java => UIInventoryUpdater.java} | 7 +- .../java/JavaDeclareRecipesTranslator.java | 11 +- .../java/JavaRespawnTranslator.java | 2 +- .../player/JavaPlayerActionAckTranslator.java | 13 +- .../JavaPlayerChangeHeldItemTranslator.java | 14 +- .../window/JavaCloseWindowTranslator.java | 6 +- .../JavaConfirmTransactionTranslator.java | 11 +- .../java/window/JavaOpenWindowTranslator.java | 77 +-- .../java/window/JavaSetSlotTranslator.java | 54 +- .../window/JavaWindowItemsTranslator.java | 36 +- .../window/JavaWindowPropertyTranslator.java | 17 +- .../world/JavaNotifyClientTranslator.java | 2 +- .../java/world/JavaTradeListTranslator.java | 35 +- .../sound/BlockSoundInteractionHandler.java | 9 +- .../sound/EntitySoundInteractionHandler.java | 9 +- .../block/BucketSoundInteractionHandler.java | 2 +- .../MilkCowSoundInteractionHandler.java | 2 +- .../geysermc/connector/utils/BlockUtils.java | 2 +- .../connector/utils/InventoryUtils.java | 91 ++-- connector/src/main/resources/languages | 2 +- connector/src/main/resources/mappings | 2 +- 58 files changed, 1611 insertions(+), 1859 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/inventory/Container.java create mode 100644 connector/src/main/java/org/geysermc/connector/inventory/EnchantmentInventory.java create mode 100644 connector/src/main/java/org/geysermc/connector/inventory/FurnaceInventory.java create mode 100644 connector/src/main/java/org/geysermc/connector/inventory/GeyserItemStack.java create mode 100644 connector/src/main/java/org/geysermc/connector/inventory/MerchantContainer.java delete mode 100644 connector/src/main/java/org/geysermc/connector/network/session/cache/InventoryCache.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockItemStackRequestTranslator.java delete mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/AnvilInventoryTranslator.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/BedrockContainerSlot.java delete mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/BlockInventoryTranslator.java delete mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/BrewingInventoryTranslator.java delete mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/CraftingInventoryTranslator.java delete mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/EnchantmentInventoryTranslator.java delete mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/FurnaceInventoryTranslator.java delete mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/GrindstoneInventoryTranslator.java delete mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/SmithingInventoryTranslator.java delete mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/action/ClickPlan.java delete mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/action/InventoryActionDataTranslator.java rename connector/src/main/java/org/geysermc/connector/network/translators/inventory/{action => click}/Click.java (66%) create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/ClickPlan.java rename connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/{CursorInventoryUpdater.java => UIInventoryUpdater.java} (89%) diff --git a/connector/src/main/java/org/geysermc/connector/entity/Entity.java b/connector/src/main/java/org/geysermc/connector/entity/Entity.java index 5e825e89..57c827a9 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/Entity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/Entity.java @@ -50,6 +50,7 @@ import org.geysermc.connector.entity.attribute.Attribute; import org.geysermc.connector.entity.attribute.AttributeType; import org.geysermc.connector.entity.living.ArmorStandEntity; import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.inventory.PlayerInventory; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.item.ItemRegistry; import org.geysermc.connector.utils.AttributeUtils; @@ -288,11 +289,12 @@ public class Entity { // Shield code if (session.getPlayerEntity().getEntityId() == entityId && metadata.getFlags().getFlag(EntityFlag.SNEAKING)) { - if ((session.getInventory().getItemInHand() != null && session.getInventory().getItemInHand().getId() == ItemRegistry.SHIELD.getJavaId()) || - (session.getInventoryCache().getPlayerInventory().getItem(45) != null && session.getInventoryCache().getPlayerInventory().getItem(45).getId() == ItemRegistry.SHIELD.getJavaId())) { + PlayerInventory playerInv = session.getPlayerInventory(); + if ((playerInv.getItemInHand().getId() == ItemRegistry.SHIELD.getJavaId()) || + (playerInv.getOffhand().getId() == ItemRegistry.SHIELD.getJavaId())) { ClientPlayerUseItemPacket useItemPacket; metadata.getFlags().setFlag(EntityFlag.BLOCKING, true); - if (session.getInventory().getItemInHand() != null && session.getInventory().getItemInHand().getId() == ItemRegistry.SHIELD.getJavaId()) { + if (playerInv.getItemInHand().getId() == ItemRegistry.SHIELD.getJavaId()) { useItemPacket = new ClientPlayerUseItemPacket(Hand.MAIN_HAND); } // Else we just assume it's the offhand, to simplify logic and to assure the packet gets sent diff --git a/connector/src/main/java/org/geysermc/connector/inventory/Container.java b/connector/src/main/java/org/geysermc/connector/inventory/Container.java new file mode 100644 index 00000000..acf450e1 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/inventory/Container.java @@ -0,0 +1,69 @@ +/* + * 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.inventory; + +import com.github.steveice10.mc.protocol.data.game.window.WindowType; +import lombok.Getter; +import lombok.NonNull; +import org.geysermc.connector.network.translators.inventory.InventoryTranslator; + +/** + * Combination of {@link Inventory} and {@link PlayerInventory} + */ +@Getter +public class Container extends Inventory { + private final PlayerInventory playerInventory; + private final int containerSize; + + public Container(String title, int id, WindowType windowType, int size, PlayerInventory playerInventory) { + super(title, id, windowType, size); + this.playerInventory = playerInventory; + this.containerSize = this.size + InventoryTranslator.PLAYER_INVENTORY_SIZE; + } + + @Override + public GeyserItemStack getItem(int slot) { + if (slot < this.size) { + return super.getItem(slot); + } else { + return playerInventory.getItem(slot - this.size + InventoryTranslator.PLAYER_INVENTORY_OFFSET); + } + } + + @Override + public void setItem(int slot, @NonNull GeyserItemStack item) { + if (slot < this.size) { + super.setItem(slot, item); + } else { + playerInventory.setItem(slot - this.size + InventoryTranslator.PLAYER_INVENTORY_OFFSET, item); + } + } + + @Override + public int getSize() { + return this.containerSize; + } +} diff --git a/connector/src/main/java/org/geysermc/connector/inventory/EnchantmentInventory.java b/connector/src/main/java/org/geysermc/connector/inventory/EnchantmentInventory.java new file mode 100644 index 00000000..65debc48 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/inventory/EnchantmentInventory.java @@ -0,0 +1,40 @@ +/* + * 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.inventory; + +import com.github.steveice10.mc.protocol.data.game.window.WindowType; +import com.nukkitx.protocol.bedrock.data.inventory.EnchantOptionData; +import lombok.Getter; + +@Getter +public class EnchantmentInventory extends Inventory { + private EnchantOptionData[] enchantOptions; + + public EnchantmentInventory(String title, int id, WindowType windowType, int size) { + super(title, id, windowType, size); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/inventory/FurnaceInventory.java b/connector/src/main/java/org/geysermc/connector/inventory/FurnaceInventory.java new file mode 100644 index 00000000..4dc098d8 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/inventory/FurnaceInventory.java @@ -0,0 +1,41 @@ +/* + * 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.inventory; + +import com.github.steveice10.mc.protocol.data.game.window.WindowType; +import lombok.Getter; +import lombok.Setter; + +@Getter +public class FurnaceInventory extends Inventory { + @Setter + private int test; + + public FurnaceInventory(String title, int id, WindowType windowType, int size) { + super(title, id, windowType, size); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/inventory/GeyserItemStack.java b/connector/src/main/java/org/geysermc/connector/inventory/GeyserItemStack.java new file mode 100644 index 00000000..c935fcdb --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/inventory/GeyserItemStack.java @@ -0,0 +1,123 @@ +/* + * 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.inventory; + +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.inventory.ItemData; +import lombok.Data; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.item.ItemEntry; +import org.geysermc.connector.network.translators.item.ItemRegistry; +import org.geysermc.connector.network.translators.item.ItemTranslator; + +@Data +public class GeyserItemStack { + public static final GeyserItemStack EMPTY = new GeyserItemStack(0, 0, null); + + private final int id; + private int amount; + private CompoundTag nbt; + private int netId; + + public GeyserItemStack(int id) { + this(id, 1); + } + + public GeyserItemStack(int id, int amount) { + this(id, amount, null); + } + + public GeyserItemStack(int id, int amount, CompoundTag nbt) { + this(id, amount, nbt, 1); + } + + public GeyserItemStack(int id, int amount, CompoundTag nbt, int netId) { + this.id = id; + this.amount = amount; + this.nbt = nbt; + this.netId = netId; + } + + public int getId() { + return isEmpty() ? 0 : id; + } + + public int getAmount() { + return isEmpty() ? 0 : amount; + } + + public CompoundTag getNbt() { + return isEmpty() ? null : nbt; + } + + public int getNetId() { + return isEmpty() ? 0 : netId; + } + + public void add(int add) { + amount += add; + } + + public void sub(int sub) { + amount -= sub; + } + + public static GeyserItemStack from(ItemStack itemStack) { + return from(itemStack, 1); + } + + public static GeyserItemStack from(ItemStack itemStack, int netId) { + return itemStack == null ? EMPTY : new GeyserItemStack(itemStack.getId(), itemStack.getAmount(), itemStack.getNbt(), netId); + } + + public ItemStack getItemStack() { + return isEmpty() ? null : new ItemStack(id, amount, nbt); + } + + public ItemData getItemData(GeyserSession session) { + ItemData itemData = ItemTranslator.translateToBedrock(session, getItemStack()); + itemData.setNetId(getNetId()); + return itemData; + } + + public ItemEntry getItemEntry() { + return ItemRegistry.ITEM_ENTRIES.get(getId()); + } + + public boolean isEmpty() { + return amount <= 0 || id == 0; + } + + public GeyserItemStack copy() { + return copy(amount); + } + + public GeyserItemStack copy(int newAmount) { + return isEmpty() ? EMPTY : new GeyserItemStack(id, newAmount, nbt == null ? null : nbt.clone(), netId); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/inventory/Inventory.java b/connector/src/main/java/org/geysermc/connector/inventory/Inventory.java index 539fe1e2..f4aea4c9 100644 --- a/connector/src/main/java/org/geysermc/connector/inventory/Inventory.java +++ b/connector/src/main/java/org/geysermc/connector/inventory/Inventory.java @@ -25,23 +25,19 @@ package org.geysermc.connector.inventory; -import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; import com.github.steveice10.mc.protocol.data.game.window.WindowType; import com.nukkitx.math.vector.Vector3i; import lombok.Getter; +import lombok.NonNull; import lombok.Setter; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.Arrays; public class Inventory { @Getter protected int id; - @Getter - @Setter - protected boolean open; - @Getter protected WindowType windowType; @@ -52,8 +48,7 @@ public class Inventory { @Setter protected String title; - @Setter - protected ItemStack[] items; + protected GeyserItemStack[] items; @Getter @Setter @@ -64,27 +59,30 @@ public class Inventory { protected long holderId = -1; @Getter - protected AtomicInteger transactionId = new AtomicInteger(1); + protected short transactionId = 0; - public Inventory(int id, WindowType windowType, int size) { + protected Inventory(int id, WindowType windowType, int size) { this("Inventory", id, windowType, size); } - public Inventory(String title, int id, WindowType windowType, int size) { + protected Inventory(String title, int id, WindowType windowType, int size) { this.title = title; this.id = id; this.windowType = windowType; this.size = size; - this.items = new ItemStack[size]; + this.items = new GeyserItemStack[size]; + Arrays.fill(items, GeyserItemStack.EMPTY); } - public ItemStack getItem(int slot) { + public GeyserItemStack getItem(int slot) { return items[slot]; } - public void setItem(int slot, ItemStack item) { - if (item != null && (item.getId() == 0 || item.getAmount() < 1)) - item = null; + public void setItem(int slot, @NonNull GeyserItemStack item) { items[slot] = item; } + + public short getNextTransactionId() { + return ++transactionId; + } } diff --git a/connector/src/main/java/org/geysermc/connector/inventory/MerchantContainer.java b/connector/src/main/java/org/geysermc/connector/inventory/MerchantContainer.java new file mode 100644 index 00000000..03ae8ac3 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/inventory/MerchantContainer.java @@ -0,0 +1,44 @@ +/* + * 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.inventory; + +import com.github.steveice10.mc.protocol.data.game.window.VillagerTrade; +import com.github.steveice10.mc.protocol.data.game.window.WindowType; +import lombok.Getter; +import lombok.Setter; +import org.geysermc.connector.entity.Entity; + +@Getter +@Setter +public class MerchantContainer extends Container { + private Entity villager; + private VillagerTrade[] villagerTrades; + + public MerchantContainer(String title, int id, WindowType windowType, int size, PlayerInventory playerInventory) { + super(title, id, windowType, size, playerInventory); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/inventory/PlayerInventory.java b/connector/src/main/java/org/geysermc/connector/inventory/PlayerInventory.java index 225335a9..e6aeb5ca 100644 --- a/connector/src/main/java/org/geysermc/connector/inventory/PlayerInventory.java +++ b/connector/src/main/java/org/geysermc/connector/inventory/PlayerInventory.java @@ -25,8 +25,8 @@ package org.geysermc.connector.inventory; -import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; import lombok.Getter; +import lombok.NonNull; import lombok.Setter; public class PlayerInventory extends Inventory { @@ -40,20 +40,24 @@ public class PlayerInventory extends Inventory { private int heldItemSlot; @Getter - private ItemStack cursor; + @Setter + @NonNull + private GeyserItemStack cursor = GeyserItemStack.EMPTY; public PlayerInventory() { super(0, null, 46); heldItemSlot = 0; } - public void setCursor(ItemStack stack) { - if (stack != null && (stack.getId() == 0 || stack.getAmount() < 1)) - stack = null; - cursor = stack; - } - - public ItemStack getItemInHand() { + public GeyserItemStack getItemInHand() { return items[36 + heldItemSlot]; } + + public void setItemInHand(@NonNull GeyserItemStack item) { + items[36 + heldItemSlot] = item; + } + + public GeyserItemStack getOffhand() { + return items[45]; + } } diff --git a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java index 1a0bbfb2..1d8120f3 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java @@ -32,6 +32,7 @@ import com.github.steveice10.mc.protocol.MinecraftConstants; import com.github.steveice10.mc.protocol.MinecraftProtocol; import com.github.steveice10.mc.protocol.data.SubProtocol; import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; +import com.github.steveice10.mc.protocol.data.game.recipe.Recipe; import com.github.steveice10.mc.protocol.data.game.window.VillagerTrade; import com.github.steveice10.mc.protocol.data.message.MessageSerializer; import com.github.steveice10.mc.protocol.packet.handshake.client.HandshakePacket; @@ -49,11 +50,14 @@ import com.nukkitx.protocol.bedrock.BedrockServerSession; import com.nukkitx.protocol.bedrock.data.*; import com.nukkitx.protocol.bedrock.data.command.CommandPermission; import com.nukkitx.protocol.bedrock.packet.*; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2LongMap; import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; +import lombok.AccessLevel; import lombok.Getter; import lombok.Setter; import org.geysermc.common.window.CustomFormWindow; @@ -63,6 +67,7 @@ import org.geysermc.connector.command.CommandSender; import org.geysermc.connector.common.AuthType; import org.geysermc.connector.entity.Entity; import org.geysermc.connector.entity.PlayerEntity; +import org.geysermc.connector.inventory.Inventory; import org.geysermc.connector.inventory.PlayerInventory; import org.geysermc.connector.network.remote.RemoteServer; import org.geysermc.connector.network.session.auth.AuthData; @@ -71,7 +76,6 @@ import org.geysermc.connector.network.session.cache.*; import org.geysermc.connector.network.translators.BiomeTranslator; import org.geysermc.connector.network.translators.EntityIdentifierRegistry; import org.geysermc.connector.network.translators.PacketTranslatorRegistry; -import org.geysermc.connector.network.translators.inventory.EnchantmentInventoryTranslator; import org.geysermc.connector.network.translators.item.ItemRegistry; import org.geysermc.connector.network.translators.world.block.BlockTranslator; import org.geysermc.connector.utils.*; @@ -83,6 +87,12 @@ import java.net.InetSocketAddress; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.spec.InvalidKeySpecException; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; import java.util.*; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.atomic.AtomicInteger; @@ -100,11 +110,20 @@ public class GeyserSession implements CommandSender { private BedrockClientData clientData; private PlayerEntity playerEntity; - private PlayerInventory inventory; + + private final PlayerInventory playerInventory; + @Setter + private Inventory openInventory; + + private final AtomicInteger itemNetId = new AtomicInteger(1); + + @Getter(AccessLevel.NONE) + private final Object inventoryLock = new Object(); + @Getter(AccessLevel.NONE) + private CompletableFuture inventoryFuture; private ChunkCache chunkCache; private EntityCache entityCache; - private InventoryCache inventoryCache; private WorldCache worldCache; private WindowCache windowCache; @Setter @@ -170,9 +189,6 @@ public class GeyserSession implements CommandSender { @Setter private Entity ridingVehicleEntity; - @Setter - private int craftSlot = 0; - @Setter private long lastWindowCloseTime = 0; @@ -187,10 +203,8 @@ public class GeyserSession implements CommandSender { @Setter private long lastInteractedVillagerEid; - /** - * Stores the enchantment information the client has received if they are in an enchantment table GUI - */ - private final EnchantmentInventoryTranslator.EnchantmentSlotData[] enchantmentSlotData = new EnchantmentInventoryTranslator.EnchantmentSlotData[3]; + @Setter + private Int2ObjectMap craftingRecipes; /** * The current attack speed of the player. Used for sending proper cooldown timings. @@ -282,18 +296,19 @@ public class GeyserSession implements CommandSender { this.chunkCache = new ChunkCache(this); this.entityCache = new EntityCache(this); - this.inventoryCache = new InventoryCache(this); this.worldCache = new WorldCache(this); this.windowCache = new WindowCache(this); this.playerEntity = new PlayerEntity(new GameProfile(UUID.randomUUID(), "unknown"), 1, 1, Vector3f.ZERO, Vector3f.ZERO, Vector3f.ZERO); - this.inventory = new PlayerInventory(); + + this.playerInventory = new PlayerInventory(); + this.openInventory = null; + this.inventoryFuture = CompletableFuture.completedFuture(null); + this.craftingRecipes = new Int2ObjectOpenHashMap<>(); this.spawned = false; this.loggedIn = false; - this.inventoryCache.getInventories().put(0, inventory); - bedrockServerSession.addDisconnectHandler(disconnectReason -> { connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.network.disconnect", bedrockServerSession.getAddress().getAddress(), disconnectReason)); @@ -529,7 +544,6 @@ public class GeyserSession implements CommandSender { this.chunkCache = null; this.entityCache = null; this.worldCache = null; - this.inventoryCache = null; this.windowCache = null; closed = true; @@ -638,10 +652,47 @@ public class GeyserSession implements CommandSender { startGamePacket.setBlockPalette(BlockTranslator.BLOCKS); startGamePacket.setItemEntries(ItemRegistry.ITEMS); startGamePacket.setVanillaVersion("*"); + // startGamePacket.setMovementServerAuthoritative(true); + startGamePacket.setInventoriesServerAuthoritative(true); startGamePacket.setAuthoritativeMovementMode(AuthoritativeMovementMode.CLIENT); upstream.sendPacket(startGamePacket); } + /** + * Adds a new inventory task. + * Inventory tasks are executed one at a time, in order. + * + * @param task the task to run + */ + public void addInventoryTask(Runnable task) { + synchronized (inventoryLock) { + System.out.println("new task " + task.toString()); + inventoryFuture = inventoryFuture.thenRun(task).exceptionally(throwable -> { + GeyserConnector.getInstance().getLogger().error("Error processing inventory task", throwable.getCause()); + return null; + }); + } + } + + /** + * Adds a new inventory task with a delay. + * The delay is achieved by scheduling with the Geyser general thread pool. + * Inventory tasks are executed one at a time, in order. + * + * @param task the delayed task to run + * @param delayMillis delay in milliseconds + */ + public void addInventoryTask(Runnable task, long delayMillis) { + synchronized (inventoryLock) { + System.out.println("new delayed task " + task.toString()); + Executor delayedExecutor = command -> GeyserConnector.getInstance().getGeneralThreadPool().schedule(command, delayMillis, TimeUnit.MILLISECONDS); + inventoryFuture = inventoryFuture.thenRunAsync(task, delayedExecutor).exceptionally(throwable -> { + GeyserConnector.getInstance().getLogger().error("Error processing inventory task", throwable.getCause()); + return null; + }); + } + } + public boolean confirmTeleport(Vector3d position) { if (teleportCache != null) { if (!teleportCache.canConfirm(position)) { diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/InventoryCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/InventoryCache.java deleted file mode 100644 index 032f6402..00000000 --- a/connector/src/main/java/org/geysermc/connector/network/session/cache/InventoryCache.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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.session.cache; - -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -import lombok.Getter; -import lombok.Setter; -import org.geysermc.connector.inventory.Inventory; -import org.geysermc.connector.network.session.GeyserSession; - -public class InventoryCache { - - private GeyserSession session; - - @Getter - @Setter - private Inventory openInventory; - - @Getter - private Int2ObjectMap inventories = new Int2ObjectOpenHashMap<>(); - - public InventoryCache(GeyserSession session) { - this.session = session; - } - - public Inventory getPlayerInventory() { - return inventories.get(0); - } - - public void cacheInventory(Inventory inventory) { - inventories.put(inventory.getId(), inventory); - } - - public void uncacheInventory(int id) { - inventories.remove(id); - } -} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockContainerCloseTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockContainerCloseTranslator.java index 00905f6d..5571ff8b 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockContainerCloseTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockContainerCloseTranslator.java @@ -38,24 +38,24 @@ public class BedrockContainerCloseTranslator extends PacketTranslator { + session.setLastWindowCloseTime(0); + byte windowId = packet.getId(); + + if (windowId == -1 && session.getOpenInventory() != null) { + windowId = (byte) session.getOpenInventory().getId(); } - } - if (windowId == 0 || (openInventory != null && openInventory.getId() == windowId)) { - ClientCloseWindowPacket closeWindowPacket = new ClientCloseWindowPacket(windowId); - session.getDownstream().getSession().send(closeWindowPacket); - InventoryUtils.closeInventory(session, windowId); - } + Inventory openInventory = session.getOpenInventory(); + if (openInventory != null && windowId == openInventory.getId()) { + System.out.println(packet); + ClientCloseWindowPacket closeWindowPacket = new ClientCloseWindowPacket(windowId); + session.getDownstream().getSession().send(closeWindowPacket); + InventoryUtils.closeInventory(session, windowId); + } - //Client wants close confirmation - session.sendUpstreamPacket(packet); + //Client wants close confirmation + session.sendUpstreamPacket(packet); + }); } } 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 b81025be..9dd0f199 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,7 +25,6 @@ package org.geysermc.connector.network.translators.bedrock; -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; @@ -43,23 +42,23 @@ import com.nukkitx.protocol.bedrock.data.inventory.ContainerId; import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket; import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket; +import com.nukkitx.protocol.bedrock.data.inventory.InventoryActionData; +import com.nukkitx.protocol.bedrock.data.inventory.InventorySource; import com.nukkitx.protocol.bedrock.packet.InventoryTransactionPacket; import com.nukkitx.protocol.bedrock.packet.LevelEventPacket; import org.geysermc.connector.entity.CommandBlockMinecartEntity; import org.geysermc.connector.entity.Entity; import org.geysermc.connector.entity.ItemFrameEntity; import org.geysermc.connector.entity.living.merchant.AbstractMerchantEntity; -import org.geysermc.connector.inventory.Inventory; +import org.geysermc.connector.inventory.GeyserItemStack; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; -import org.geysermc.connector.network.translators.inventory.InventoryTranslator; import org.geysermc.connector.network.translators.item.ItemEntry; import org.geysermc.connector.network.translators.item.ItemRegistry; import org.geysermc.connector.network.translators.sound.EntitySoundInteractionHandler; import org.geysermc.connector.network.translators.world.block.BlockTranslator; import org.geysermc.connector.utils.BlockUtils; -import org.geysermc.connector.utils.InventoryUtils; import java.util.concurrent.TimeUnit; @@ -70,15 +69,36 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator { + if (session.getPlayerInventory().getHeldItemSlot() != containerAction.getSlot()) + return; + if (session.getPlayerInventory().getItemInHand().isEmpty()) + return; + + boolean dropAll = worldAction.getToItem().getCount() > 1; + ClientPlayerActionPacket dropAllPacket = new ClientPlayerActionPacket( + dropAll ? PlayerAction.DROP_ITEM_STACK : PlayerAction.DROP_ITEM, + new Position(0, 0, 0), + BlockFace.DOWN + ); + session.sendDownstreamPacket(dropAllPacket); + + if (dropAll) { + session.getPlayerInventory().setItemInHand(GeyserItemStack.EMPTY); + } else { + session.getPlayerInventory().getItemInHand().sub(1); + } + }); + } + } break; case INVENTORY_MISMATCH: - Inventory inv = session.getInventoryCache().getOpenInventory(); - if (inv == null) inv = session.getInventory(); - InventoryTranslator.INVENTORY_TRANSLATORS.get(inv.getWindowType()).updateInventory(session, inv); - InventoryUtils.updateCursor(session); break; case ITEM_USE: switch (packet.getActionType()) { @@ -164,9 +184,8 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator { + + @Override + public void translate(ItemStackRequestPacket packet, GeyserSession session) { + Inventory inventory = session.getOpenInventory(); + if (inventory == null) + return; + + InventoryTranslator translator = InventoryTranslator.INVENTORY_TRANSLATORS.get(inventory.getWindowType()); + session.addInventoryTask(() -> translator.translateRequests(session, inventory, packet.getRequests())); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockMobEquipmentTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockMobEquipmentTranslator.java index a220e389..ade94919 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockMobEquipmentTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockMobEquipmentTranslator.java @@ -40,12 +40,12 @@ public class BedrockMobEquipmentTranslator extends PacketTranslator 8 || - packet.getContainerId() != ContainerId.INVENTORY || session.getInventory().getHeldItemSlot() == packet.getHotbarSlot()) { + packet.getContainerId() != ContainerId.INVENTORY || session.getPlayerInventory().getHeldItemSlot() == packet.getHotbarSlot()) { // For the last condition - Don't update the slot if the slot is the same - not Java Edition behavior and messes with plugins such as Grief Prevention return; } - session.getInventory().setHeldItemSlot(packet.getHotbarSlot()); + session.getPlayerInventory().setHeldItemSlot(packet.getHotbarSlot()); ClientPlayerChangeHeldItemPacket changeHeldItemPacket = new ClientPlayerChangeHeldItemPacket(packet.getHotbarSlot()); session.sendDownstreamPacket(changeHeldItemPacket); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/BedrockEntityEventTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/BedrockEntityEventTranslator.java index 18fd6614..eaf352da 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/BedrockEntityEventTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/BedrockEntityEventTranslator.java @@ -26,12 +26,13 @@ package org.geysermc.connector.network.translators.bedrock.entity; import com.github.steveice10.mc.protocol.data.game.window.VillagerTrade; -import com.github.steveice10.mc.protocol.data.game.window.WindowType; import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientSelectTradePacket; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.packet.EntityEventPacket; import org.geysermc.connector.entity.Entity; +import org.geysermc.connector.inventory.GeyserItemStack; import org.geysermc.connector.inventory.Inventory; +import org.geysermc.connector.inventory.MerchantContainer; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; @@ -47,20 +48,25 @@ public class BedrockEntityEventTranslator extends PacketTranslator { + ClientSelectTradePacket selectTradePacket = new ClientSelectTradePacket(packet.getData()); + session.getDownstream().getSession().send(selectTradePacket); + }); - Entity villager = session.getPlayerEntity(); - Inventory openInventory = session.getInventoryCache().getOpenInventory(); - if (openInventory != null && openInventory.getWindowType() == WindowType.MERCHANT) { - VillagerTrade[] trades = session.getVillagerTrades(); - if (trades != null && packet.getData() >= 0 && packet.getData() < trades.length) { - VillagerTrade trade = session.getVillagerTrades()[packet.getData()]; - openInventory.setItem(2, trade.getOutput()); - villager.getMetadata().put(EntityData.TRADE_XP, trade.getXp() + villager.getMetadata().getInt(EntityData.TRADE_XP)); - villager.updateBedrockMetadata(session); + session.addInventoryTask(() -> { + Entity villager = session.getPlayerEntity(); + Inventory openInventory = session.getOpenInventory(); + if (openInventory instanceof MerchantContainer) { + MerchantContainer merchantInventory = (MerchantContainer) openInventory; + VillagerTrade[] trades = merchantInventory.getVillagerTrades(); + if (trades != null && packet.getData() >= 0 && packet.getData() < trades.length) { + VillagerTrade trade = merchantInventory.getVillagerTrades()[packet.getData()]; + openInventory.setItem(2, GeyserItemStack.from(trade.getOutput())); + villager.getMetadata().put(EntityData.TRADE_XP, trade.getXp() + villager.getMetadata().getInt(EntityData.TRADE_XP)); + villager.updateBedrockMetadata(session); + } } - } + }, 100); return; } session.getConnector().getLogger().debug("Did not translate incoming EntityEventPacket: " + packet.toString()); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockInteractTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockInteractTranslator.java index c5d6f2dd..ff261aba 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockInteractTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockInteractTranslator.java @@ -60,7 +60,7 @@ public class BedrockInteractTranslator extends PacketTranslator switch (packet.getAction()) { case INTERACT: - if (session.getInventory().getItem(session.getInventory().getHeldItemSlot() + 36).getId() == ItemRegistry.SHIELD.getJavaId()) { + if (session.getPlayerInventory().getItemInHand().getId() == ItemRegistry.SHIELD.getJavaId()) { break; } ClientPlayerInteractEntityPacket interactPacket = new ClientPlayerInteractEntityPacket((int) entity.getEntityId(), @@ -135,14 +135,15 @@ public class BedrockInteractTranslator extends PacketTranslator } break; case OPEN_INVENTORY: - if (!session.getInventory().isOpen()) { + if (session.getOpenInventory() == null) { + session.setOpenInventory(session.getPlayerInventory()); + ContainerOpenPacket containerOpenPacket = new ContainerOpenPacket(); containerOpenPacket.setId((byte) 0); containerOpenPacket.setType(ContainerType.INVENTORY); containerOpenPacket.setUniqueEntityId(-1); containerOpenPacket.setBlockPosition(entity.getPosition().toInt()); session.sendUpstreamPacket(containerOpenPacket); - session.getInventory().setOpen(true); } break; } 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 deleted file mode 100644 index ab266fae..00000000 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/AnvilInventoryTranslator.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * 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; - -import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; -import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientRenameItemPacket; -import com.github.steveice10.opennbt.tag.builtin.CompoundTag; -import com.google.gson.JsonSyntaxException; -import com.nukkitx.nbt.NbtMap; -import com.nukkitx.protocol.bedrock.data.inventory.*; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; -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; -import java.util.stream.Collectors; - -public class AnvilInventoryTranslator extends BlockInventoryTranslator { - public AnvilInventoryTranslator() { - super(3, "minecraft:anvil[facing=north]", ContainerType.ANVIL, new CursorInventoryUpdater()); - } - - @Override - public int bedrockSlotToJava(InventoryActionData action) { - if (action.getSource().getContainerId() == ContainerId.UI) { - switch (action.getSlot()) { - case 1: - return 0; - case 2: - return 1; - case 50: - return 2; - } - } - if (action.getSource().getContainerId() == ContainerId.ANVIL_RESULT) { - return 2; - } - 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) { - InventoryActionData anvilResult = null; - InventoryActionData anvilInput = null; - for (InventoryActionData 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; - NbtMap tag = itemName.getTag(); - if (tag != null && tag.containsKey("display")) { - String name = tag.getCompound("display").getString("Name"); - try { - Component component = GsonComponentSerializer.gson().deserialize(name); - rename = LegacyComponentSerializer.legacySection().serialize(component); - } catch (JsonSyntaxException e) { - rename = name; - } - } else { - rename = ""; - } - ClientRenameItemPacket renameItemPacket = new ClientRenameItemPacket(rename); - session.sendDownstreamPacket(renameItemPacket); - } - if (anvilResult != null) { - //Strip unnecessary actions - List strippedActions = actions.stream() - .filter(action -> action.getSource().getContainerId() == ContainerId.ANVIL_RESULT - || (action.getSource().getType() == InventorySource.Type.CONTAINER - && !(action.getSource().getContainerId() == ContainerId.UI && action.getSlot() != 0))) - .collect(Collectors.toList()); - super.translateActions(session, inventory, strippedActions); - return; - } - - super.translateActions(session, inventory, actions); - } - - @Override - public void updateSlot(GeyserSession session, Inventory inventory, int slot) { - if (slot == 0) { - ItemStack item = inventory.getItem(slot); - if (item != null) { - String rename; - CompoundTag tag = item.getNbt(); - if (tag != null) { - CompoundTag displayTag = tag.get("display"); - if (displayTag != null && displayTag.contains("Name")) { - String itemName = displayTag.get("Name").getValue().toString(); - try { - Component component = GsonComponentSerializer.gson().deserialize(itemName); - rename = LegacyComponentSerializer.legacySection().serialize(component); - } catch (JsonSyntaxException e) { - rename = itemName; - } - } else { - rename = ""; - } - } else { - rename = ""; - } - ClientRenameItemPacket renameItemPacket = new ClientRenameItemPacket(rename); - session.sendDownstreamPacket(renameItemPacket); - } - } - super.updateSlot(session, inventory, slot); - } -} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/BaseInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/BaseInventoryTranslator.java index 6f00fc4d..7d54667b 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/BaseInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/BaseInventoryTranslator.java @@ -25,15 +25,15 @@ package org.geysermc.connector.network.translators.inventory; -import com.nukkitx.protocol.bedrock.data.inventory.ContainerId; -import com.nukkitx.protocol.bedrock.data.inventory.InventoryActionData; +import com.github.steveice10.mc.protocol.data.game.window.WindowType; +import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; +import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData; +import org.geysermc.connector.inventory.Container; 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.action.InventoryActionDataTranslator; -import java.util.List; - -public abstract class BaseInventoryTranslator extends InventoryTranslator{ +public abstract class BaseInventoryTranslator extends InventoryTranslator { BaseInventoryTranslator(int size) { super(size); } @@ -44,15 +44,18 @@ public abstract class BaseInventoryTranslator extends InventoryTranslator{ } @Override - public int bedrockSlotToJava(InventoryActionData action) { - int slotnum = action.getSlot(); - if (action.getSource().getContainerId() == ContainerId.INVENTORY) { - //hotbar - if (slotnum >= 9) { - return slotnum + this.size - 9; - } else { - return slotnum + this.size + 27; - } + public int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData) { + int slotnum = slotInfoData.getSlot(); + switch (slotInfoData.getContainer()) { + case HOTBAR_AND_INVENTORY: + case HOTBAR: + case INVENTORY: + //hotbar + if (slotnum >= 9) { + return slotnum + this.size - 9; + } else { + return slotnum + this.size + 27; + } } return slotnum; } @@ -70,13 +73,26 @@ public abstract class BaseInventoryTranslator extends InventoryTranslator{ return slot; } + @Override + public BedrockContainerSlot javaSlotToBedrockContainer(int slot) { + if (slot >= this.size) { + final int tmp = slot - this.size; + if (tmp < 27) { + return new BedrockContainerSlot(ContainerSlotType.INVENTORY, tmp + 9); + } else { + return new BedrockContainerSlot(ContainerSlotType.HOTBAR, tmp - 27); + } + } + throw new IllegalArgumentException("Unknown bedrock slot"); + } + @Override public SlotType getSlotType(int javaSlot) { return SlotType.NORMAL; } @Override - public void translateActions(GeyserSession session, Inventory inventory, List actions) { - InventoryActionDataTranslator.translate(this, session, inventory, actions); + public Inventory createInventory(String name, int windowId, WindowType windowType, PlayerInventory playerInventory) { + return new Container(name, windowId, windowType, this.size, playerInventory); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/BedrockContainerSlot.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/BedrockContainerSlot.java new file mode 100644 index 00000000..b3a09e16 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/BedrockContainerSlot.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; + +import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; +import lombok.Value; + +@Value +public class BedrockContainerSlot { + ContainerSlotType container; + int slot; +} 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 deleted file mode 100644 index 4b8b57e8..00000000 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/BlockInventoryTranslator.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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; - -import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; -import org.geysermc.connector.inventory.Inventory; -import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.world.block.BlockTranslator; -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 BaseInventoryTranslator { - private final InventoryHolder holder; - private final InventoryUpdater updater; - - public BlockInventoryTranslator(int size, String javaBlockIdentifier, ContainerType containerType, InventoryUpdater updater) { - super(size); - int javaBlockState = BlockTranslator.getJavaBlockState(javaBlockIdentifier); - int blockId = BlockTranslator.getBedrockBlockId(javaBlockState); - this.holder = new BlockInventoryHolder(blockId, containerType); - this.updater = updater; - } - - @Override - public void prepareInventory(GeyserSession session, Inventory inventory) { - holder.prepareInventory(this, session, inventory); - } - - @Override - public void openInventory(GeyserSession session, Inventory inventory) { - holder.openInventory(this, session, inventory); - } - - @Override - public void closeInventory(GeyserSession session, Inventory inventory) { - 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/BrewingInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/BrewingInventoryTranslator.java deleted file mode 100644 index 89cdbe8d..00000000 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/BrewingInventoryTranslator.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * 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; - -import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; -import com.nukkitx.protocol.bedrock.data.inventory.InventoryActionData; -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 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 - public void openInventory(GeyserSession session, Inventory inventory) { - super.openInventory(session, inventory); - ContainerSetDataPacket dataPacket = new ContainerSetDataPacket(); - dataPacket.setWindowId((byte) inventory.getId()); - dataPacket.setProperty(ContainerSetDataPacket.BREWING_STAND_FUEL_TOTAL); - dataPacket.setValue(20); - session.sendUpstreamPacket(dataPacket); - } - - @Override - public void updateProperty(GeyserSession session, Inventory inventory, int key, int value) { - ContainerSetDataPacket dataPacket = new ContainerSetDataPacket(); - dataPacket.setWindowId((byte) inventory.getId()); - switch (key) { - case 0: - dataPacket.setProperty(ContainerSetDataPacket.BREWING_STAND_BREW_TIME); - break; - case 1: - dataPacket.setProperty(ContainerSetDataPacket.BREWING_STAND_FUEL_AMOUNT); - break; - default: - return; - } - dataPacket.setValue(value); - session.sendUpstreamPacket(dataPacket); - } - - @Override - public int bedrockSlotToJava(InventoryActionData action) { - final int slot = super.bedrockSlotToJava(action); - switch (slot) { - case 0: - return 3; - case 1: - return 0; - case 2: - return 1; - case 3: - return 2; - default: - return slot; - } - } - - @Override - public int javaSlotToBedrock(int slot) { - switch (slot) { - case 0: - return 1; - case 1: - return 2; - case 2: - return 3; - case 3: - return 0; - } - return super.javaSlotToBedrock(slot); - } -} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/ChestInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/ChestInventoryTranslator.java index 152f4a85..df97563e 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/ChestInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/ChestInventoryTranslator.java @@ -25,14 +25,11 @@ package org.geysermc.connector.network.translators.inventory; -import com.nukkitx.protocol.bedrock.data.inventory.InventoryActionData; +import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; import org.geysermc.connector.inventory.Inventory; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.inventory.updater.ChestInventoryUpdater; import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater; -import org.geysermc.connector.utils.InventoryUtils; - -import java.util.List; public abstract class ChestInventoryTranslator extends BaseInventoryTranslator { private final InventoryUpdater updater; @@ -53,17 +50,10 @@ public abstract class ChestInventoryTranslator extends BaseInventoryTranslator { } @Override - public void translateActions(GeyserSession session, Inventory inventory, List actions) { - for (InventoryActionData action : actions) { - if (action.getSource().getContainerId() == inventory.getId()) { - if (action.getSlot() >= size) { - updateInventory(session, inventory); - InventoryUtils.updateCursor(session); - return; - } - } + public BedrockContainerSlot javaSlotToBedrockContainer(int javaSlot) { + if (javaSlot < this.size) { + return new BedrockContainerSlot(ContainerSlotType.CONTAINER, javaSlot); } - - super.translateActions(session, inventory, actions); + return super.javaSlotToBedrockContainer(javaSlot); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/CraftingInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/CraftingInventoryTranslator.java deleted file mode 100644 index b260565b..00000000 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/CraftingInventoryTranslator.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * 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; - -import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; -import com.nukkitx.protocol.bedrock.data.inventory.ContainerId; -import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; -import com.nukkitx.protocol.bedrock.data.inventory.InventoryActionData; -import com.nukkitx.protocol.bedrock.data.inventory.InventorySource; -import org.geysermc.connector.inventory.Inventory; -import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.inventory.updater.CursorInventoryUpdater; -import org.geysermc.connector.utils.InventoryUtils; - -import java.util.List; - -public class CraftingInventoryTranslator extends BlockInventoryTranslator { - public CraftingInventoryTranslator() { - super(10, "minecraft:crafting_table", ContainerType.WORKBENCH, new CursorInventoryUpdater()); - } - - @Override - public int bedrockSlotToJava(InventoryActionData action) { - if (action.getSlot() == 50) { - // Slot 50 is used for crafting with a controller. - return 0; - } - - if (action.getSource().getContainerId() == ContainerId.UI) { - int slotnum = action.getSlot(); - if (slotnum >= 32 && 42 >= slotnum) { - return slotnum - 31; - } - } - return super.bedrockSlotToJava(action); - } - - @Override - public int javaSlotToBedrock(int slot) { - if (slot < size) { - return slot == 0 ? 50 : slot + 31; - } - return super.javaSlotToBedrock(slot); - } - - @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 (InventoryActionData 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/EnchantmentInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/EnchantmentInventoryTranslator.java deleted file mode 100644 index cbcdce10..00000000 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/EnchantmentInventoryTranslator.java +++ /dev/null @@ -1,267 +0,0 @@ -/* - * 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; - -import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientClickWindowButtonPacket; -import com.nukkitx.nbt.NbtMap; -import com.nukkitx.nbt.NbtMapBuilder; -import com.nukkitx.nbt.NbtType; -import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; -import com.nukkitx.protocol.bedrock.data.inventory.InventoryActionData; -import com.nukkitx.protocol.bedrock.data.inventory.ItemData; -import com.nukkitx.protocol.bedrock.packet.InventoryContentPacket; -import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; -import org.geysermc.connector.common.ChatColor; -import org.geysermc.connector.inventory.Inventory; -import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater; -import org.geysermc.connector.network.translators.item.ItemTranslator; -import org.geysermc.connector.utils.InventoryUtils; -import org.geysermc.connector.utils.LocaleUtils; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -/** - * A temporary reconstruction of the enchantment table UI until our inventory rewrite is complete. - * The enchantment table on Bedrock without server authoritative inventories doesn't tell us which button is pressed - * when selecting an enchantment. - */ -public class EnchantmentInventoryTranslator extends BlockInventoryTranslator { - - private static final int DYE_ID = 351; - private static final short LAPIS_DAMAGE = 4; - private static final int ENCHANTED_BOOK_ID = 403; - - public EnchantmentInventoryTranslator(InventoryUpdater updater) { - super(2, "minecraft:hopper[enabled=false,facing=down]", ContainerType.HOPPER, updater); - } - - @Override - public void translateActions(GeyserSession session, Inventory inventory, List actions) { - for (InventoryActionData action : actions) { - if (action.getSource().getContainerId() == inventory.getId()) { - // This is the hopper UI - switch (action.getSlot()) { - case 1: - // Don't allow the slot to be put through if the item isn't lapis - if ((action.getToItem().getId() != DYE_ID - && action.getToItem().getDamage() != LAPIS_DAMAGE) && action.getToItem() != ItemData.AIR) { - updateInventory(session, inventory); - InventoryUtils.updateCursor(session); - return; - } - break; - case 2: - case 3: - case 4: - // The books here act as buttons - ClientClickWindowButtonPacket packet = new ClientClickWindowButtonPacket(inventory.getId(), action.getSlot() - 2); - session.sendDownstreamPacket(packet); - updateInventory(session, inventory); - InventoryUtils.updateCursor(session); - return; - default: - break; - } - } - } - - super.translateActions(session, inventory, actions); - } - - @Override - public void updateInventory(GeyserSession session, Inventory inventory) { - super.updateInventory(session, inventory); - ItemData[] items = new ItemData[5]; - items[0] = ItemTranslator.translateToBedrock(session, inventory.getItem(0)); - items[1] = ItemTranslator.translateToBedrock(session, inventory.getItem(1)); - for (int i = 0; i < 3; i++) { - items[i + 2] = session.getEnchantmentSlotData()[i].getItem() != null ? session.getEnchantmentSlotData()[i].getItem() : createEnchantmentBook(); - } - - InventoryContentPacket contentPacket = new InventoryContentPacket(); - contentPacket.setContainerId(inventory.getId()); - contentPacket.setContents(items); - session.sendUpstreamPacket(contentPacket); - } - - @Override - public void updateProperty(GeyserSession session, Inventory inventory, int key, int value) { - int bookSlotToUpdate; - switch (key) { - case 0: - case 1: - case 2: - // Experience required - bookSlotToUpdate = key; - session.getEnchantmentSlotData()[bookSlotToUpdate].setExperienceRequired(value); - break; - case 4: - case 5: - case 6: - // Enchantment name - bookSlotToUpdate = key - 4; - if (value != -1) { - session.getEnchantmentSlotData()[bookSlotToUpdate].setEnchantmentType(EnchantmentTableEnchantments.values()[value - 1]); - } else { - // -1 means no enchantment specified - session.getEnchantmentSlotData()[bookSlotToUpdate].setEnchantmentType(null); - } - break; - case 7: - case 8: - case 9: - // Enchantment level - bookSlotToUpdate = key - 7; - session.getEnchantmentSlotData()[bookSlotToUpdate].setEnchantmentLevel(value); - break; - default: - return; - } - updateEnchantmentBook(session, inventory, bookSlotToUpdate); - } - - @Override - public void openInventory(GeyserSession session, Inventory inventory) { - super.openInventory(session, inventory); - for (int i = 0; i < session.getEnchantmentSlotData().length; i++) { - session.getEnchantmentSlotData()[i] = new EnchantmentSlotData(); - } - } - - @Override - public void closeInventory(GeyserSession session, Inventory inventory) { - super.closeInventory(session, inventory); - Arrays.fill(session.getEnchantmentSlotData(), null); - } - - private ItemData createEnchantmentBook() { - NbtMapBuilder root = NbtMap.builder(); - NbtMapBuilder display = NbtMap.builder(); - - display.putString("Name", ChatColor.RESET + "No Enchantment"); - - root.put("display", display.build()); - return ItemData.of(ENCHANTED_BOOK_ID, (short) 0, 1, root.build()); - } - - private void updateEnchantmentBook(GeyserSession session, Inventory inventory, int slot) { - NbtMapBuilder root = NbtMap.builder(); - NbtMapBuilder display = NbtMap.builder(); - EnchantmentSlotData data = session.getEnchantmentSlotData()[slot]; - if (data.getEnchantmentType() != null) { - display.putString("Name", ChatColor.ITALIC + data.getEnchantmentType().toString(session) + - (data.getEnchantmentLevel() != -1 ? " " + toRomanNumeral(session, data.getEnchantmentLevel()) : "") + "?"); - } else { - display.putString("Name", ChatColor.RESET + "No Enchantment"); - } - - display.putList("Lore", NbtType.STRING, Collections.singletonList(ChatColor.DARK_GRAY + data.getExperienceRequired() + "xp")); - root.put("display", display.build()); - ItemData book = ItemData.of(ENCHANTED_BOOK_ID, (short) 0, 1, root.build()); - - InventorySlotPacket slotPacket = new InventorySlotPacket(); - slotPacket.setContainerId(inventory.getId()); - slotPacket.setSlot(slot + 2); - slotPacket.setItem(book); - session.sendUpstreamPacket(slotPacket); - data.setItem(book); - } - - private String toRomanNumeral(GeyserSession session, int level) { - return LocaleUtils.getLocaleString("enchantment.level." + level, - session.getClientData().getLanguageCode()); - } - - /** - * Stores the data of each slot in an enchantment table - */ - @NoArgsConstructor - @Getter - @Setter - @ToString - public static class EnchantmentSlotData { - private EnchantmentTableEnchantments enchantmentType = null; - private int enchantmentLevel = 0; - private int experienceRequired = 0; - private ItemData item; - } - - /** - * Classifies enchantments by Java order - */ - public enum EnchantmentTableEnchantments { - PROTECTION, - FIRE_PROTECTION, - FEATHER_FALLING, - BLAST_PROTECTION, - PROJECTILE_PROTECTION, - RESPIRATION, - AQUA_AFFINITY, - THORNS, - DEPTH_STRIDER, - FROST_WALKER, - BINDING_CURSE, - SHARPNESS, - SMITE, - BANE_OF_ARTHROPODS, - KNOCKBACK, - FIRE_ASPECT, - LOOTING, - SWEEPING, - EFFICIENCY, - SILK_TOUCH, - UNBREAKING, - FORTUNE, - POWER, - PUNCH, - FLAME, - INFINITY, - LUCK_OF_THE_SEA, - LURE, - LOYALTY, - IMPALING, - RIPTIDE, - CHANNELING, - MENDING, - VANISHING_CURSE, // After this is not documented - MULTISHOT, - PIERCING, - QUICK_CHARGE, - SOUL_SPEED; - - public String toString(GeyserSession session) { - return LocaleUtils.getLocaleString("enchantment.minecraft." + this.toString().toLowerCase(), - session.getClientData().getLanguageCode()); - } - } -} 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 deleted file mode 100644 index 1f148e02..00000000 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/FurnaceInventoryTranslator.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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; - -import com.github.steveice10.mc.protocol.data.game.window.WindowType; -import com.nukkitx.protocol.bedrock.data.inventory.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, new ContainerInventoryUpdater()); - } - - @Override - public void updateProperty(GeyserSession session, Inventory inventory, int key, int value) { - ContainerSetDataPacket dataPacket = new ContainerSetDataPacket(); - dataPacket.setWindowId((byte) inventory.getId()); - switch (key) { - case 0: - dataPacket.setProperty(ContainerSetDataPacket.FURNACE_LIT_TIME); - break; - case 1: - dataPacket.setProperty(ContainerSetDataPacket.FURNACE_LIT_DURATION); - break; - case 2: - dataPacket.setProperty(ContainerSetDataPacket.FURNACE_TICK_COUNT); - if (inventory.getWindowType() == WindowType.BLAST_FURNACE || inventory.getWindowType() == WindowType.SMOKER) { - value *= 2; - } - break; - default: - return; - } - dataPacket.setValue(value); - session.sendUpstreamPacket(dataPacket); - } - - @Override - public SlotType getSlotType(int javaSlot) { - if (javaSlot == 2) - return SlotType.FURNACE_OUTPUT; - return SlotType.NORMAL; - } -} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/GrindstoneInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/GrindstoneInventoryTranslator.java deleted file mode 100644 index 4b4a1246..00000000 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/GrindstoneInventoryTranslator.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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; - -import com.nukkitx.protocol.bedrock.data.inventory.ContainerId; -import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; -import com.nukkitx.protocol.bedrock.data.inventory.InventoryActionData; -import org.geysermc.connector.network.translators.inventory.updater.CursorInventoryUpdater; - -public class GrindstoneInventoryTranslator extends BlockInventoryTranslator { - - public GrindstoneInventoryTranslator() { - super(3, "minecraft:grindstone[face=floor,facing=north]", ContainerType.GRINDSTONE, new CursorInventoryUpdater()); - } - - @Override - public int bedrockSlotToJava(InventoryActionData action) { - final int slot = super.bedrockSlotToJava(action); - if (action.getSource().getContainerId() == ContainerId.UI) { - switch (slot) { - case 16: - return 0; - case 17: - return 1; - case 50: - return 2; - default: - return slot; - } - } return slot; - } - - @Override - public int javaSlotToBedrock(int slot) { - switch (slot) { - case 0: - return 16; - case 1: - return 17; - case 2: - return 50; - } - return super.javaSlotToBedrock(slot); - } - -} 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 e44e4bd0..b4a12b00 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 @@ -25,18 +25,26 @@ 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.window.WindowType; -import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; -import com.nukkitx.protocol.bedrock.data.inventory.InventoryActionData; +import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientCreativeInventoryActionPacket; +import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; +import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData; +import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.*; +import com.nukkitx.protocol.bedrock.packet.ItemStackRequestPacket; +import com.nukkitx.protocol.bedrock.packet.ItemStackResponsePacket; import lombok.AllArgsConstructor; +import org.geysermc.connector.inventory.GeyserItemStack; 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.updater.ContainerInventoryUpdater; -import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater; +import org.geysermc.connector.network.translators.inventory.click.Click; +import org.geysermc.connector.network.translators.inventory.click.ClickPlan; +import org.geysermc.connector.network.translators.item.ItemRegistry; +import org.geysermc.connector.network.translators.item.ItemTranslator; +import org.geysermc.connector.utils.InventoryUtils; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; @AllArgsConstructor public abstract class InventoryTranslator { @@ -50,12 +58,13 @@ public abstract class InventoryTranslator { put(WindowType.GENERIC_9X4, new DoubleChestInventoryTranslator(36)); put(WindowType.GENERIC_9X5, new DoubleChestInventoryTranslator(45)); put(WindowType.GENERIC_9X6, new DoubleChestInventoryTranslator(54)); - put(WindowType.BREWING_STAND, new BrewingInventoryTranslator()); + /*put(WindowType.BREWING_STAND, new BrewingInventoryTranslator()); put(WindowType.ANVIL, new AnvilInventoryTranslator()); put(WindowType.CRAFTING, new CraftingInventoryTranslator()); - put(WindowType.GRINDSTONE, new GrindstoneInventoryTranslator()); + put(WindowType.GRINDSTONE, new GrindstoneInventoryTranslator());*/ put(WindowType.MERCHANT, new MerchantInventoryTranslator()); - put(WindowType.SMITHING, new SmithingInventoryTranslator()); + /*put(WindowType.SMITHING, new SmithingInventoryTranslator()); + //put(WindowType.ENCHANTMENT, new EnchantmentInventoryTranslator()); //TODO InventoryTranslator furnace = new FurnaceInventoryTranslator(); put(WindowType.FURNACE, furnace); @@ -63,14 +72,15 @@ public abstract class InventoryTranslator { put(WindowType.SMOKER, furnace); InventoryUpdater containerUpdater = new ContainerInventoryUpdater(); - put(WindowType.ENCHANTMENT, new EnchantmentInventoryTranslator(containerUpdater)); //TODO put(WindowType.GENERIC_3X3, new BlockInventoryTranslator(9, "minecraft:dispenser[facing=north,triggered=false]", ContainerType.DISPENSER, containerUpdater)); put(WindowType.HOPPER, new BlockInventoryTranslator(5, "minecraft:hopper[enabled=false,facing=down]", ContainerType.HOPPER, containerUpdater)); put(WindowType.SHULKER_BOX, new BlockInventoryTranslator(27, "minecraft:shulker_box[facing=north]", ContainerType.CONTAINER, containerUpdater)); - //put(WindowType.BEACON, new BlockInventoryTranslator(1, "minecraft:beacon", ContainerType.BEACON)); //TODO + //put(WindowType.BEACON, new BlockInventoryTranslator(1, "minecraft:beacon", ContainerType.BEACON)); //TODO*/ } }; + public static final int PLAYER_INVENTORY_SIZE = 36; + public static final int PLAYER_INVENTORY_OFFSET = 9; public final int size; public abstract void prepareInventory(GeyserSession session, Inventory inventory); @@ -79,8 +89,428 @@ public abstract class InventoryTranslator { public abstract void updateProperty(GeyserSession session, Inventory inventory, int key, int value); public abstract void updateInventory(GeyserSession session, Inventory inventory); public abstract void updateSlot(GeyserSession session, Inventory inventory, int slot); - public abstract int bedrockSlotToJava(InventoryActionData action); - public abstract int javaSlotToBedrock(int slot); + public abstract int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData); + public abstract int javaSlotToBedrock(int javaSlot); //TODO + public abstract BedrockContainerSlot javaSlotToBedrockContainer(int javaSlot); //TODO public abstract SlotType getSlotType(int javaSlot); - public abstract void translateActions(GeyserSession session, Inventory inventory, List actions); + public abstract Inventory createInventory(String name, int windowId, WindowType windowType, PlayerInventory playerInventory); + + public void translateRequests(GeyserSession session, Inventory inventory, List requests) { + ItemStackResponsePacket responsePacket = new ItemStackResponsePacket(); + for (ItemStackRequestPacket.Request request : requests) { + if (request.getActions().length > 0) { + StackRequestActionData firstAction = request.getActions()[0]; + if (firstAction.getType() == StackRequestActionType.CRAFT_RECIPE || firstAction.getType() == StackRequestActionType.CRAFT_RECIPE_AUTO) { + responsePacket.getEntries().add(translateCraftingRequest(session, inventory, request)); + } else if (firstAction.getType() == StackRequestActionType.CRAFT_CREATIVE) { + responsePacket.getEntries().add(translateCreativeRequest(session, inventory, request)); + } else { + responsePacket.getEntries().add(translateRequest(session, inventory, request)); + } + } else { + responsePacket.getEntries().add(rejectRequest(request)); + } + } + session.sendUpstreamPacket(responsePacket); + System.out.println(responsePacket); + } + + public ItemStackResponsePacket.Response translateRequest(GeyserSession session, Inventory inventory, ItemStackRequestPacket.Request request) { + System.out.println(request); + ClickPlan plan = new ClickPlan(session, this, inventory); + for (StackRequestActionData action : request.getActions()) { + GeyserItemStack cursor = session.getPlayerInventory().getCursor(); + switch (action.getType()) { + case TAKE: + case PLACE: { + TransferStackRequestActionData transferAction = (TransferStackRequestActionData) action; + if (!(checkNetId(session, inventory, transferAction.getSource()) && checkNetId(session, inventory, transferAction.getDestination()))) + return rejectRequest(request); + + if (isCursor(transferAction.getSource()) && isCursor(transferAction.getDestination())) { //??? + return rejectRequest(request); + } else if (isCursor(transferAction.getSource())) { //releasing cursor + int sourceAmount = cursor.getAmount(); + int destSlot = bedrockSlotToJava(transferAction.getDestination()); + if (transferAction.getCount() == sourceAmount) { //release all + plan.add(Click.LEFT, destSlot); + } else { //release some + for (int i = 0; i < transferAction.getCount(); i++) { + plan.add(Click.RIGHT, destSlot); + } + } + } else if (isCursor(transferAction.getDestination())) { //picking up into cursor + int sourceSlot = bedrockSlotToJava(transferAction.getSource()); + GeyserItemStack sourceItem = plan.getItem(sourceSlot); + int sourceAmount = sourceItem.getAmount(); + if (cursor.isEmpty()) { //picking up into empty cursor + if (transferAction.getCount() == sourceAmount) { //pickup all + plan.add(Click.LEFT, sourceSlot); + } else if (transferAction.getCount() == sourceAmount - (sourceAmount / 2)) { //larger half; simple right click + plan.add(Click.RIGHT, sourceSlot); + } else { //pickup some; not a simple right click + plan.add(Click.LEFT, sourceSlot); //first pickup all + for (int i = 0; i < sourceAmount - transferAction.getCount(); i++) { + plan.add(Click.RIGHT, sourceSlot); //release extra items back into source slot + } + } + } else { //pickup into non-empty cursor + if (!InventoryUtils.canStack(cursor, plan.getItem(sourceSlot))) { //doesn't make sense, reject + return rejectRequest(request); + } + if (transferAction.getCount() != sourceAmount) { //TODO: handle partially picking up into non-empty cursor (temp slot) + return rejectRequest(request); + } + plan.add(Click.LEFT, sourceSlot); //release cursor onto source slot + plan.add(Click.LEFT, sourceSlot); //pickup combined cursor and source + } + } else { //transfer from one slot to another + if (!cursor.isEmpty()) { //TODO: handle slot transfer when cursor is already in use (temp slot) + return rejectRequest(request); + } + int sourceSlot = bedrockSlotToJava(transferAction.getSource()); + int sourceAmount = plan.getItem(sourceSlot).getAmount(); + int destSlot = bedrockSlotToJava(transferAction.getDestination()); + if (transferAction.getCount() == sourceAmount) { //transfer all + plan.add(Click.LEFT, sourceSlot); //pickup source + plan.add(Click.LEFT, destSlot); //let go of all items and done + } else { //transfer some + //try to transfer items with least clicks possible + int halfSource = sourceAmount / 2; //smaller half + int holding; + if (transferAction.getCount() <= halfSource) { //faster to take only half + plan.add(Click.RIGHT, sourceSlot); + holding = halfSource; + } else { //need all + plan.add(Click.LEFT, sourceSlot); + holding = sourceAmount; + } + if (transferAction.getCount() > holding / 2) { //faster to release extra items onto source or dest slot? + for (int i = 0; i < holding - transferAction.getCount(); i++) { + plan.add(Click.RIGHT, sourceSlot); //prepare cursor + } + plan.add(Click.LEFT, destSlot); //release cursor onto dest slot + } else { + for (int i = 0; i < transferAction.getCount(); i++) { + plan.add(Click.RIGHT, destSlot); //right click until transfer goal is met + } + plan.add(Click.LEFT, sourceSlot); //return extra items to source slot + } + } + } + break; + } + case SWAP: { //TODO + SwapStackRequestActionData swapAction = (SwapStackRequestActionData) action; + if (!(checkNetId(session, inventory, swapAction.getSource()) && checkNetId(session, inventory, swapAction.getDestination()))) + return rejectRequest(request); + + if (isCursor(swapAction.getSource()) && isCursor(swapAction.getDestination())) { //??? + return rejectRequest(request); + } else if (isCursor(swapAction.getSource())) { //swap cursor + int destSlot = bedrockSlotToJava(swapAction.getDestination()); + if (InventoryUtils.canStack(cursor, plan.getItem(destSlot))) { //TODO: cannot simply swap if cursor stacks with slot (temp slot) + return rejectRequest(request); + } + plan.add(Click.LEFT, destSlot); + } else if (isCursor(swapAction.getDestination())) { //swap cursor + int sourceSlot = bedrockSlotToJava(swapAction.getSource()); + if (InventoryUtils.canStack(cursor, plan.getItem(sourceSlot))) { //TODO + return rejectRequest(request); + } + plan.add(Click.LEFT, sourceSlot); + } else { + int sourceSlot = bedrockSlotToJava(swapAction.getSource()); + int destSlot = bedrockSlotToJava(swapAction.getDestination()); + if (!cursor.isEmpty()) { //TODO: (temp slot) + return rejectRequest(request); + } + if (sourceSlot == destSlot) { //doesn't make sense + return rejectRequest(request); + } + if (InventoryUtils.canStack(plan.getItem(sourceSlot), plan.getItem(destSlot))) { //TODO: (temp slot) + return rejectRequest(request); + } + plan.add(Click.LEFT, sourceSlot); //pickup source into cursor + plan.add(Click.LEFT, destSlot); //swap cursor with dest slot + plan.add(Click.LEFT, sourceSlot); //release cursor onto source + } + break; + } + case DROP: { + DropStackRequestActionData dropAction = (DropStackRequestActionData) action; + if (!checkNetId(session, inventory, dropAction.getSource())) + return rejectRequest(request); + + if (isCursor(dropAction.getSource())) { //clicking outside of window + int sourceAmount = plan.getCursor().getAmount(); + if (dropAction.getCount() == sourceAmount) { //drop all + plan.add(Click.LEFT_OUTSIDE, Click.OUTSIDE_SLOT); + } else { //drop some + for (int i = 0; i < dropAction.getCount(); i++) { + plan.add(Click.RIGHT_OUTSIDE, Click.OUTSIDE_SLOT); //drop one until goal is met + } + } + } else { //dropping from inventory + int sourceSlot = bedrockSlotToJava(dropAction.getSource()); + int sourceAmount = plan.getItem(sourceSlot).getAmount(); + if (dropAction.getCount() == sourceAmount && sourceAmount > 1) { //dropping all? (prefer DROP_ONE if only one) + plan.add(Click.DROP_ALL, sourceSlot); + } else { //drop some + for (int i = 0; i < dropAction.getCount(); i++) { + plan.add(Click.DROP_ONE, sourceSlot); //drop one until goal is met + } + } + } + break; + } + case CRAFT_CREATIVE: { + CraftCreativeStackRequestActionData creativeAction = (CraftCreativeStackRequestActionData) action; + System.out.println(creativeAction.getCreativeItemNetworkId()); + } + default: + return rejectRequest(request); + } + } + plan.execute(false); + return acceptRequest(request, makeContainerEntries(session, inventory, plan.getAffectedSlots())); + } + + public ItemStackResponsePacket.Response translateCraftingRequest(GeyserSession session, Inventory inventory, ItemStackRequestPacket.Request request) { + System.out.println(request); + + int recipeId = 0; + int resultSize = 0; + boolean autoCraft; + CraftState craftState = CraftState.START; + + int leftover = 0; + ClickPlan plan = new ClickPlan(session, this, inventory); + for (StackRequestActionData action : request.getActions()) { + switch (action.getType()) { + case CRAFT_RECIPE: { + CraftRecipeStackRequestActionData craftAction = (CraftRecipeStackRequestActionData) action; + if (craftState != CraftState.START) { + return rejectRequest(request); + } + craftState = CraftState.RECIPE_ID; + recipeId = craftAction.getRecipeNetworkId(); + System.out.println(session.getCraftingRecipes().get(recipeId).toString()); + autoCraft = false; + break; + } + /*case CRAFT_RECIPE_AUTO: { + AutoCraftRecipeStackRequestActionData autoCraftAction = (AutoCraftRecipeStackRequestActionData) action; + if (craftState != CraftState.START) { + return rejectRequest(request); + } + craftState = CraftState.RECIPE_ID; + recipeId = autoCraftAction.getRecipeNetworkId(); + autoCraft = true; + //TODO: reject transaction if crafting grid is not clear + break; + }*/ + case CRAFT_RESULTS_DEPRECATED: { + CraftResultsDeprecatedStackRequestActionData deprecatedCraftAction = (CraftResultsDeprecatedStackRequestActionData) action; + if (craftState != CraftState.RECIPE_ID) { + return rejectRequest(request); + } + craftState = CraftState.DEPRECATED; + + if (deprecatedCraftAction.getResultItems().length != 1) { + return rejectRequest(request); + } + resultSize = deprecatedCraftAction.getResultItems()[0].getCount(); + if (resultSize <= 0) { + return rejectRequest(request); + } + break; + } + case CONSUME: { + ConsumeStackRequestActionData consumeAction = (ConsumeStackRequestActionData) action; + if (craftState != CraftState.DEPRECATED && craftState != CraftState.INGREDIENTS) { + return rejectRequest(request); + } + craftState = CraftState.INGREDIENTS; + break; + } + case TAKE: + case PLACE: { + TransferStackRequestActionData transferAction = (TransferStackRequestActionData) action; + if (craftState != CraftState.INGREDIENTS && craftState != CraftState.TRANSFER) { + return rejectRequest(request); + } + craftState = CraftState.TRANSFER; + + if (transferAction.getSource().getContainer() != ContainerSlotType.CREATIVE_OUTPUT) { + return rejectRequest(request); + } + if (transferAction.getCount() <= 0) { + return rejectRequest(request); + } + + int sourceSlot = bedrockSlotToJava(transferAction.getSource()); + if (isCursor(transferAction.getDestination())) { + plan.add(Click.LEFT, sourceSlot); + craftState = CraftState.DONE; + } else { + int destSlot = bedrockSlotToJava(transferAction.getDestination()); + if (leftover != 0) { + if (transferAction.getCount() > leftover) { + return rejectRequest(request); + } + if (transferAction.getCount() == leftover) { + plan.add(Click.LEFT, destSlot); + } else { + for (int i = 0; i < transferAction.getCount(); i++) { + plan.add(Click.RIGHT, destSlot); + } + } + leftover -= transferAction.getCount(); + break; + } + + int remainder = transferAction.getCount() % resultSize; + int timesToCraft = transferAction.getCount() / resultSize; + for (int i = 0; i < timesToCraft; i++) { + plan.add(Click.LEFT, sourceSlot); + plan.add(Click.LEFT, destSlot); + } + if (remainder > 0) { + plan.add(Click.LEFT, 0); + for (int i = 0; i < remainder; i++) { + plan.add(Click.RIGHT, destSlot); + } + leftover = resultSize - remainder; + } + } + break; + } + default: + return rejectRequest(request); + } + } + plan.execute(false); + Set affectedSlots = plan.getAffectedSlots(); + affectedSlots.addAll(Arrays.asList(1, 2, 3, 4)); //TODO: crafting grid + return acceptRequest(request, makeContainerEntries(session, inventory, affectedSlots)); + } + + public ItemStackResponsePacket.Response translateCreativeRequest(GeyserSession session, Inventory inventory, ItemStackRequestPacket.Request request) { + int creativeId = 0; + CraftState craftState = CraftState.START; + for (StackRequestActionData action : request.getActions()) { + switch (action.getType()) { + case CRAFT_CREATIVE: { + CraftCreativeStackRequestActionData creativeAction = (CraftCreativeStackRequestActionData) action; + if (craftState != CraftState.START) { + return rejectRequest(request); + } + craftState = CraftState.RECIPE_ID; + + creativeId = creativeAction.getCreativeItemNetworkId(); + break; + } + case CRAFT_RESULTS_DEPRECATED: { + CraftResultsDeprecatedStackRequestActionData deprecatedCraftAction = (CraftResultsDeprecatedStackRequestActionData) action; + if (craftState != CraftState.RECIPE_ID) { + return rejectRequest(request); + } + craftState = CraftState.DEPRECATED; + break; + } + case TAKE: + case PLACE: { + TransferStackRequestActionData transferAction = (TransferStackRequestActionData) action; + if (craftState != CraftState.DEPRECATED) { + return rejectRequest(request); + } + craftState = CraftState.TRANSFER; + + if (transferAction.getSource().getContainer() != ContainerSlotType.CREATIVE_OUTPUT) { + return rejectRequest(request); + } + if (isCursor(transferAction.getDestination())) { + session.getPlayerInventory().setCursor(GeyserItemStack.from(ItemTranslator.translateToJava(ItemRegistry.CREATIVE_ITEMS[creativeId]), session.getItemNetId().getAndIncrement())); //TODO + return acceptRequest(request, makeContainerEntries(session, inventory, Collections.emptySet())); + } else { + int javaSlot = bedrockSlotToJava(transferAction.getDestination()); + ItemStack javaItem = ItemTranslator.translateToJava(ItemRegistry.CREATIVE_ITEMS[creativeId - 1]); //TODO + inventory.setItem(javaSlot, GeyserItemStack.from(javaItem, session.getItemNetId().getAndIncrement())); + ClientCreativeInventoryActionPacket creativeActionPacket = new ClientCreativeInventoryActionPacket( + javaSlot, + javaItem + ); + session.sendDownstreamPacket(creativeActionPacket); + Set affectedSlots = Collections.singleton(javaSlot); + return acceptRequest(request, makeContainerEntries(session, inventory, affectedSlots)); + } + } + default: + return rejectRequest(request); + } + } + return rejectRequest(request); + } + + public static ItemStackResponsePacket.Response acceptRequest(ItemStackRequestPacket.Request request, List containerEntries) { + return new ItemStackResponsePacket.Response(true, request.getRequestId(), containerEntries); + } + + public static ItemStackResponsePacket.Response rejectRequest(ItemStackRequestPacket.Request request) { + new Throwable("DEBUGGING: ItemStackRequest rejected").printStackTrace(); //TODO: temporary debugging + return new ItemStackResponsePacket.Response(false, request.getRequestId(), Collections.emptyList()); + } + + public boolean checkNetId(GeyserSession session, Inventory inventory, StackRequestSlotInfoData slotInfoData) { + if (slotInfoData.getStackNetworkId() < 0) + return true; + + GeyserItemStack currentItem = isCursor(slotInfoData) ? session.getPlayerInventory().getCursor() : inventory.getItem(bedrockSlotToJava(slotInfoData)); + return currentItem.getNetId() == slotInfoData.getStackNetworkId(); + } + + public List makeContainerEntries(GeyserSession session, Inventory inventory, Set affectedSlots) { + Map> containerMap = new HashMap<>(); + for (int slot : affectedSlots) { + BedrockContainerSlot bedrockSlot = javaSlotToBedrockContainer(slot); + List list = containerMap.computeIfAbsent(bedrockSlot.getContainer(), k -> new ArrayList<>()); + list.add(makeItemEntry(session, bedrockSlot.getSlot(), inventory.getItem(slot))); + } + + List containerEntries = new ArrayList<>(); + for (Map.Entry> entry : containerMap.entrySet()) { + containerEntries.add(new ItemStackResponsePacket.ContainerEntry(entry.getKey(), entry.getValue())); + } + + ItemStackResponsePacket.ItemEntry cursorEntry = makeItemEntry(session, 0, session.getPlayerInventory().getCursor()); + containerEntries.add(new ItemStackResponsePacket.ContainerEntry(ContainerSlotType.CURSOR, Collections.singletonList(cursorEntry))); + + return containerEntries; + } + + public static ItemStackResponsePacket.ItemEntry makeItemEntry(GeyserSession session, int bedrockSlot, GeyserItemStack itemStack) { + ItemStackResponsePacket.ItemEntry itemEntry; + if (!itemStack.isEmpty()) { + int newNetId = session.getItemNetId().getAndIncrement(); + itemStack.setNetId(newNetId); + itemEntry = new ItemStackResponsePacket.ItemEntry((byte) bedrockSlot, (byte) bedrockSlot, (byte) itemStack.getAmount(), newNetId); + } else { + itemEntry = new ItemStackResponsePacket.ItemEntry((byte) bedrockSlot, (byte) bedrockSlot, (byte) 0, 0); + } + return itemEntry; + } + + private static boolean isCursor(StackRequestSlotInfoData slotInfoData) { + return slotInfoData.getContainer() == ContainerSlotType.CURSOR; + } + + private enum CraftState { + START, + RECIPE_ID, + DEPRECATED, + INGREDIENTS, + TRANSFER, + DONE + } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/MerchantInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/MerchantInventoryTranslator.java index 25fdae68..c4b2fc9a 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/MerchantInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/MerchantInventoryTranslator.java @@ -25,15 +25,21 @@ package org.geysermc.connector.network.translators.inventory; -import com.nukkitx.protocol.bedrock.data.inventory.ContainerId; -import com.nukkitx.protocol.bedrock.data.inventory.InventoryActionData; -import com.nukkitx.protocol.bedrock.data.inventory.InventorySource; +import com.github.steveice10.mc.protocol.data.game.window.WindowType; +import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.protocol.bedrock.data.entity.EntityData; +import com.nukkitx.protocol.bedrock.data.entity.EntityDataMap; +import com.nukkitx.protocol.bedrock.data.entity.EntityLinkData; +import com.nukkitx.protocol.bedrock.data.inventory.*; +import com.nukkitx.protocol.bedrock.packet.SetEntityLinkPacket; +import org.geysermc.connector.entity.Entity; +import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.inventory.Inventory; +import org.geysermc.connector.inventory.MerchantContainer; +import org.geysermc.connector.inventory.PlayerInventory; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.inventory.updater.CursorInventoryUpdater; import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater; - -import java.util.List; +import org.geysermc.connector.network.translators.inventory.updater.UIInventoryUpdater; public class MerchantInventoryTranslator extends BaseInventoryTranslator { @@ -41,7 +47,7 @@ public class MerchantInventoryTranslator extends BaseInventoryTranslator { public MerchantInventoryTranslator() { super(3); - this.updater = new CursorInventoryUpdater(); + this.updater = new UIInventoryUpdater(); } @Override @@ -58,26 +64,30 @@ public class MerchantInventoryTranslator extends BaseInventoryTranslator { } @Override - public int bedrockSlotToJava(InventoryActionData action) { - switch (action.getSource().getContainerId()) { - case ContainerId.UI: - switch (action.getSlot()) { - case 4: - return 0; - case 5: - return 1; - case 50: - return 2; - } - break; - case -28: // Trading 1? + public BedrockContainerSlot javaSlotToBedrockContainer(int slot) { + switch (slot) { + case 0: + return new BedrockContainerSlot(ContainerSlotType.TRADE2_INGREDIENT1, 4); + case 1: + return new BedrockContainerSlot(ContainerSlotType.TRADE2_INGREDIENT2, 5); + case 2: + return new BedrockContainerSlot(ContainerSlotType.TRADE2_RESULT, 50); + } + return super.javaSlotToBedrockContainer(slot); + } + + @Override + public int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData) { + switch (slotInfoData.getContainer()) { + case TRADE2_INGREDIENT1: return 0; - case -29: // Trading 2? + case TRADE2_INGREDIENT2: return 1; - case -30: // Trading Output? + case TRADE2_RESULT: + case CREATIVE_OUTPUT: return 2; } - return super.bedrockSlotToJava(action); + return super.bedrockSlotToJava(slotInfoData); } @Override @@ -90,18 +100,40 @@ public class MerchantInventoryTranslator extends BaseInventoryTranslator { @Override public void prepareInventory(GeyserSession session, Inventory inventory) { + MerchantContainer merchantInventory = (MerchantContainer) inventory; + if (merchantInventory.getVillager() == null) { + long geyserId = session.getEntityCache().getNextEntityId().incrementAndGet(); + Vector3f pos = session.getPlayerEntity().getPosition().sub(0, 3, 0); + EntityDataMap metadata = new EntityDataMap(); + metadata.put(EntityData.SCALE, 0f); + metadata.put(EntityData.BOUNDING_BOX_WIDTH, 0f); + metadata.put(EntityData.BOUNDING_BOX_HEIGHT, 0f); + + Entity villager = new Entity(0, geyserId, EntityType.VILLAGER, pos, Vector3f.ZERO, Vector3f.ZERO); + villager.setMetadata(metadata); + villager.spawnEntity(session); + + SetEntityLinkPacket linkPacket = new SetEntityLinkPacket(); + EntityLinkData.Type type = EntityLinkData.Type.PASSENGER; + linkPacket.setEntityLink(new EntityLinkData(session.getPlayerEntity().getGeyserId(), geyserId, type, true, false)); + session.sendUpstreamPacket(linkPacket); + + merchantInventory.setVillager(villager); + } } @Override public void openInventory(GeyserSession session, Inventory inventory) { - + //Handled in JavaTradeListTranslator } @Override public void closeInventory(GeyserSession session, Inventory inventory) { - session.setLastInteractedVillagerEid(-1); - session.setVillagerTrades(null); + MerchantContainer merchantInventory = (MerchantContainer) inventory; + if (merchantInventory.getVillager() != null) { + merchantInventory.getVillager().despawnEntity(session); + } } @Override @@ -115,11 +147,7 @@ public class MerchantInventoryTranslator extends BaseInventoryTranslator { } @Override - public void translateActions(GeyserSession session, Inventory inventory, List actions) { - if (actions.stream().anyMatch(a -> a.getSource().getContainerId() == -31)) { - return; - } - - super.translateActions(session, inventory, actions); + public Inventory createInventory(String name, int windowId, WindowType windowType, PlayerInventory playerInventory) { + return new MerchantContainer(name, windowId, windowType, this.size, playerInventory); } } 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 7d7673c4..48315fc1 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 @@ -25,18 +25,15 @@ 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.inventory.ContainerId; -import com.nukkitx.protocol.bedrock.data.inventory.InventoryActionData; -import com.nukkitx.protocol.bedrock.data.inventory.InventorySource; -import com.nukkitx.protocol.bedrock.data.inventory.ItemData; +import com.github.steveice10.mc.protocol.data.game.window.WindowType; +import com.nukkitx.protocol.bedrock.data.inventory.*; import com.nukkitx.protocol.bedrock.packet.InventoryContentPacket; import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket; +import com.nukkitx.protocol.bedrock.packet.ItemStackRequestPacket; 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.action.InventoryActionDataTranslator; import org.geysermc.connector.network.translators.item.ItemTranslator; import org.geysermc.connector.utils.InventoryUtils; import org.geysermc.connector.utils.LanguageUtils; @@ -59,11 +56,11 @@ public class PlayerInventoryTranslator extends InventoryTranslator { ItemData[] contents = new ItemData[36]; // Inventory for (int i = 9; i < 36; i++) { - contents[i] = ItemTranslator.translateToBedrock(session, inventory.getItem(i)); + contents[i] = inventory.getItem(i).getItemData(session); } // Hotbar for (int i = 36; i < 45; i++) { - contents[i - 36] = ItemTranslator.translateToBedrock(session, inventory.getItem(i)); + contents[i - 36] = inventory.getItem(i).getItemData(session); } inventoryContentPacket.setContents(contents); session.sendUpstreamPacket(inventoryContentPacket); @@ -73,7 +70,7 @@ public class PlayerInventoryTranslator extends InventoryTranslator { armorContentPacket.setContainerId(ContainerId.ARMOR); contents = new ItemData[4]; for (int i = 5; i < 9; i++) { - contents[i - 5] = ItemTranslator.translateToBedrock(session, inventory.getItem(i)); + contents[i - 5] = inventory.getItem(i).getItemData(session); } armorContentPacket.setContents(contents); session.sendUpstreamPacket(armorContentPacket); @@ -81,7 +78,7 @@ public class PlayerInventoryTranslator extends InventoryTranslator { // Offhand InventoryContentPacket offhandPacket = new InventoryContentPacket(); offhandPacket.setContainerId(ContainerId.OFFHAND); - offhandPacket.setContents(new ItemData[]{ItemTranslator.translateToBedrock(session, inventory.getItem(45))}); + offhandPacket.setContents(new ItemData[]{inventory.getItem(45).getItemData(session)}); session.sendUpstreamPacket(offhandPacket); } @@ -100,7 +97,7 @@ public class PlayerInventoryTranslator extends InventoryTranslator { if (session.getGameMode() == GameMode.CREATIVE) { slotPacket.setItem(UNUSUABLE_CRAFTING_SPACE_BLOCK); }else{ - slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(i))); + slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(i).getItemStack())); } session.sendUpstreamPacket(slotPacket); @@ -125,21 +122,23 @@ public class PlayerInventoryTranslator extends InventoryTranslator { slotPacket.setContainerId(ContainerId.UI); slotPacket.setSlot(slot + 27); } - slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(slot))); + slotPacket.setItem(inventory.getItem(slot).getItemData(session)); session.sendUpstreamPacket(slotPacket); } else if (slot == 45) { InventoryContentPacket offhandPacket = new InventoryContentPacket(); offhandPacket.setContainerId(ContainerId.OFFHAND); - offhandPacket.setContents(new ItemData[]{ItemTranslator.translateToBedrock(session, inventory.getItem(slot))}); + offhandPacket.setContents(new ItemData[]{inventory.getItem(slot).getItemData(session)}); session.sendUpstreamPacket(offhandPacket); } } @Override - public int bedrockSlotToJava(InventoryActionData action) { - int slotnum = action.getSlot(); - switch (action.getSource().getContainerId()) { - case ContainerId.INVENTORY: + public int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData) { + int slotnum = slotInfoData.getSlot(); + switch (slotInfoData.getContainer()) { + case HOTBAR_AND_INVENTORY: + case HOTBAR: + case INVENTORY: // Inventory if (slotnum >= 9 && slotnum <= 35) { return slotnum; @@ -149,19 +148,19 @@ public class PlayerInventoryTranslator extends InventoryTranslator { return slotnum + 36; } break; - case ContainerId.ARMOR: + case ARMOR: if (slotnum >= 0 && slotnum <= 3) { return slotnum + 5; } break; - case ContainerId.OFFHAND: + case OFFHAND: return 45; - case ContainerId.UI: + case CRAFTING_INPUT: if (slotnum >= 28 && 31 >= slotnum) { return slotnum - 27; } break; - case ContainerId.CRAFTING_RESULT: + case CREATIVE_OUTPUT: return 0; } return slotnum; @@ -169,7 +168,26 @@ public class PlayerInventoryTranslator extends InventoryTranslator { @Override public int javaSlotToBedrock(int slot) { - return slot; + return -1; + } + + @Override + public BedrockContainerSlot javaSlotToBedrockContainer(int slot) { + if (slot >= 36 && slot <= 44) { + return new BedrockContainerSlot(ContainerSlotType.HOTBAR, slot - 36); + } else if (slot >= 9 && slot <= 35) { + return new BedrockContainerSlot(ContainerSlotType.INVENTORY, slot); + } else if (slot >= 5 && slot <= 8) { + return new BedrockContainerSlot(ContainerSlotType.ARMOR, slot - 5); + } else if (slot == 45) { + return new BedrockContainerSlot(ContainerSlotType.OFFHAND, 1); + } else if (slot >= 1 && slot <= 4) { + return new BedrockContainerSlot(ContainerSlotType.CRAFTING_INPUT, slot + 27); + } else if (slot == 0) { + return new BedrockContainerSlot(ContainerSlotType.CRAFTING_OUTPUT, 0); + } else { + throw new IllegalArgumentException("Unknown bedrock slot"); + } } @Override @@ -180,52 +198,13 @@ public class PlayerInventoryTranslator extends InventoryTranslator { } @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 (InventoryActionData action : actions) { - if (action.getSource().getContainerId() == ContainerId.UI && (action.getSlot() >= 28 && 31 >= action.getSlot())) { - updateInventory(session, inventory); - InventoryUtils.updateCursor(session); - return; - } - } + public void translateRequests(GeyserSession session, Inventory inventory, List requests) { + super.translateRequests(session, inventory, requests); + } - ItemStack javaItem; - for (InventoryActionData 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 = ItemTranslator.translateToJava(action.getToItem()); - } - ClientCreativeInventoryActionPacket creativePacket = new ClientCreativeInventoryActionPacket(javaSlot, javaItem); - session.sendDownstreamPacket(creativePacket); - inventory.setItem(javaSlot, javaItem); - break; - case ContainerId.UI: - if (action.getSlot() == 0) { - session.getInventory().setCursor(ItemTranslator.translateToJava(action.getToItem())); - } - break; - case ContainerId.NONE: - if (action.getSource().getType() == InventorySource.Type.WORLD_INTERACTION - && action.getSource().getFlag() == InventorySource.Flag.DROP_ITEM) { - javaItem = ItemTranslator.translateToJava(action.getToItem()); - ClientCreativeInventoryActionPacket creativeDropPacket = new ClientCreativeInventoryActionPacket(-1, javaItem); - session.sendDownstreamPacket(creativeDropPacket); - } - break; - } - } - return; - } - - InventoryActionDataTranslator.translate(this, session, inventory, actions); + @Override + public Inventory createInventory(String name, int windowId, WindowType windowType, PlayerInventory playerInventory) { + throw new UnsupportedOperationException(); } @Override diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/SmithingInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/SmithingInventoryTranslator.java deleted file mode 100644 index f7f0acd8..00000000 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/SmithingInventoryTranslator.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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; - -import com.nukkitx.protocol.bedrock.data.inventory.ContainerId; -import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; -import com.nukkitx.protocol.bedrock.data.inventory.InventoryActionData; -import org.geysermc.connector.network.translators.inventory.updater.CursorInventoryUpdater; - -public class SmithingInventoryTranslator extends BlockInventoryTranslator { - - public SmithingInventoryTranslator() { - super(3, "minecraft:smithing_table", ContainerType.SMITHING_TABLE, new CursorInventoryUpdater()); - } - - @Override - public int bedrockSlotToJava(InventoryActionData action) { - final int slot = super.bedrockSlotToJava(action); - if (action.getSource().getContainerId() == ContainerId.UI) { - switch (slot) { - case 51: - return 0; - case 52: - return 1; - case 50: - return 2; - default: - return slot; - } - } return slot; - } - - @Override - public int javaSlotToBedrock(int slot) { - switch (slot) { - case 0: - return 51; - case 1: - return 52; - case 2: - return 50; - } - return super.javaSlotToBedrock(slot); - } - -} 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 deleted file mode 100644 index a9c1eddc..00000000 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/action/ClickPlan.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * 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 : 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.sendDownstreamPacket(clickPacket); - session.sendDownstreamPacket(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/InventoryActionDataTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/action/InventoryActionDataTranslator.java deleted file mode 100644 index 96cbd61f..00000000 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/action/InventoryActionDataTranslator.java +++ /dev/null @@ -1,338 +0,0 @@ -/* - * 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.inventory.ContainerId; -import com.nukkitx.protocol.bedrock.data.inventory.InventoryActionData; -import com.nukkitx.protocol.bedrock.data.inventory.InventorySource; -import com.nukkitx.protocol.bedrock.data.inventory.ItemData; -import org.geysermc.connector.inventory.Inventory; -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.network.translators.item.ItemTranslator; -import org.geysermc.connector.utils.InventoryUtils; - -import java.util.*; - -public class InventoryActionDataTranslator { - public static void translate(InventoryTranslator translator, GeyserSession session, Inventory inventory, List actions) { - if (actions.size() != 2) - return; - - InventoryActionData worldAction = null; - InventoryActionData cursorAction = null; - InventoryActionData containerAction = null; - boolean refresh = false; - for (InventoryActionData action : actions) { - if (action.getSource().getContainerId() == ContainerId.CRAFTING_USE_INGREDIENT) { - return; - } else if (action.getSource().getType() == InventorySource.Type.WORLD_INTERACTION) { - worldAction = action; - } else if (action.getSource().getContainerId() == ContainerId.UI && action.getSlot() == 0) { - cursorAction = action; - ItemData translatedCursor = ItemTranslator.translateToBedrock(session, session.getInventory().getCursor()); - if (!translatedCursor.equals(action.getFromItem())) { - refresh = true; - } - } else { - containerAction = action; - ItemData translatedItem = ItemTranslator.translateToBedrock(session, inventory.getItem(translator.bedrockSlotToJava(action))); - if (!translatedItem.equals(action.getFromItem())) { - refresh = true; - } - } - } - - final int craftSlot = session.getCraftSlot(); - session.setCraftSlot(0); - - if (worldAction != null) { - InventoryActionData 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.sendDownstreamPacket(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.sendDownstreamPacket(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.sendDownstreamPacket(dropPacket); - } - } - ItemStack item = inventory.getItem(javaSlot); - if (item != null) { - inventory.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.sendDownstreamPacket(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.sendDownstreamPacket(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), false); - if (cursorSlot != -1) { - plan.add(Click.LEFT, cursorSlot); - } else { - translator.updateInventory(session, inventory); - InventoryUtils.updateCursor(session); - 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(); - InventoryActionData fromAction; - InventoryActionData 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), - translator.getSlotType(fromSlot) == SlotType.OUTPUT); - if (cursorSlot != -1) { - plan.add(Click.LEFT, cursorSlot); - } else { - translator.updateInventory(session, inventory); - InventoryUtils.updateCursor(session); - 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.sendDownstreamPacket(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, boolean emptyOnly) { - /*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) { - if (emptyOnly) { - continue; - } - 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/action/Click.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/Click.java similarity index 66% rename from connector/src/main/java/org/geysermc/connector/network/translators/inventory/action/Click.java rename to connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/Click.java index 1fdfa364..d27290bf 100644 --- 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/click/Click.java @@ -23,16 +23,25 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.connector.network.translators.inventory.action; +package org.geysermc.connector.network.translators.inventory.click; import com.github.steveice10.mc.protocol.data.game.window.ClickItemParam; +import com.github.steveice10.mc.protocol.data.game.window.DropItemParam; +import com.github.steveice10.mc.protocol.data.game.window.WindowAction; 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 enum Click { + LEFT(WindowAction.CLICK_ITEM, ClickItemParam.LEFT_CLICK), + RIGHT(WindowAction.CLICK_ITEM, ClickItemParam.RIGHT_CLICK), + DROP_ONE(WindowAction.DROP_ITEM, DropItemParam.DROP_FROM_SELECTED), + DROP_ALL(WindowAction.DROP_ITEM, DropItemParam.DROP_SELECTED_STACK), + LEFT_OUTSIDE(WindowAction.CLICK_ITEM, ClickItemParam.LEFT_CLICK), + RIGHT_OUTSIDE(WindowAction.CLICK_ITEM, ClickItemParam.RIGHT_CLICK); + public static final int OUTSIDE_SLOT = -999; + + public final WindowAction windowAction; public final WindowActionParam actionParam; } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/ClickPlan.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/ClickPlan.java new file mode 100644 index 00000000..f4cf3561 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/ClickPlan.java @@ -0,0 +1,211 @@ +/* + * 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.click; + +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 it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import lombok.Value; +import org.geysermc.connector.inventory.GeyserItemStack; +import org.geysermc.connector.inventory.Inventory; +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.*; + +public class ClickPlan { + private final List plan = new ArrayList<>(); + private final Int2ObjectMap simulatedItems; + private GeyserItemStack simulatedCursor; + private boolean simulating; + + private final GeyserSession session; + private final InventoryTranslator translator; + private final Inventory inventory; + + public ClickPlan(GeyserSession session, InventoryTranslator translator, Inventory inventory) { + this.session = session; + this.translator = translator; + this.inventory = inventory; + + this.simulatedItems = new Int2ObjectOpenHashMap<>(inventory.getSize()); + this.simulatedCursor = session.getPlayerInventory().getCursor().copy(); + this.simulating = true; + } + + public void add(Click click, int slot) { + if (!simulating) + throw new UnsupportedOperationException("ClickPlan already executed"); + + if (click == Click.LEFT_OUTSIDE || click == Click.RIGHT_OUTSIDE) { + slot = Click.OUTSIDE_SLOT; + } + + ClickAction action = new ClickAction(click, slot); + plan.add(action); + simulateAction(action); + } + + public void execute(boolean refresh) { + simulating = false; + ListIterator planIter = plan.listIterator(); + while (planIter.hasNext()) { + ClickAction action = planIter.next(); + + if (action.slot != Click.OUTSIDE_SLOT && translator.getSlotType(action.slot) != SlotType.NORMAL) { + refresh = true; + } + + ItemStack clickedItemStack; + if (!planIter.hasNext() && refresh) { + clickedItemStack = InventoryUtils.REFRESH_ITEM; + } else if (action.click.windowAction == WindowAction.DROP_ITEM || action.slot == Click.OUTSIDE_SLOT) { + clickedItemStack = null; + } else { + clickedItemStack = inventory.getItem(action.slot).getItemStack(); + } + + short actionId = inventory.getNextTransactionId(); + ClientWindowActionPacket clickPacket = new ClientWindowActionPacket( + inventory.getId(), + actionId, + action.slot, + clickedItemStack, + action.click.windowAction, + action.click.actionParam + ); + + simulateAction(action); + + session.sendDownstreamPacket(clickPacket); + if (clickedItemStack == InventoryUtils.REFRESH_ITEM) { + session.sendDownstreamPacket(new ClientConfirmTransactionPacket(inventory.getId(), actionId, true)); + } + System.out.println(clickPacket); + } + } + + public GeyserItemStack getItem(int slot) { + return simulatedItems.computeIfAbsent(slot, k -> inventory.getItem(slot).copy()); + } + + public GeyserItemStack getCursor() { + return simulatedCursor; + } + + private void setItem(int slot, GeyserItemStack item) { + if (simulating) { + simulatedItems.put(slot, item); + } else { + inventory.setItem(slot, item); + } + } + + private void setCursor(GeyserItemStack item) { + if (simulating) { + simulatedCursor = item; + } else { + session.getPlayerInventory().setCursor(item); + } + } + + private void simulateAction(ClickAction action) { + GeyserItemStack cursor = simulating ? getCursor() : session.getPlayerInventory().getCursor(); + switch (action.click) { + case LEFT_OUTSIDE: + setCursor(GeyserItemStack.EMPTY); + return; + case RIGHT_OUTSIDE: + if (!cursor.isEmpty()) { + cursor.sub(1); + } + return; + } + + GeyserItemStack clicked = simulating ? getItem(action.slot) : inventory.getItem(action.slot); + if (translator.getSlotType(action.slot) == SlotType.OUTPUT) { + if (cursor.isEmpty() && !clicked.isEmpty()) { + setCursor(clicked.copy()); + } else if (InventoryUtils.canStack(cursor, clicked)) { + cursor.add(clicked.getAmount()); + } + } else { + switch (action.click) { + case LEFT: + if (!InventoryUtils.canStack(cursor, clicked)) { + setCursor(clicked); + setItem(action.slot, cursor); + } else { + setCursor(GeyserItemStack.EMPTY); + clicked.add(cursor.getAmount()); + } + break; + case RIGHT: + if (cursor.isEmpty() && !clicked.isEmpty()) { + int half = clicked.getAmount() / 2; //smaller half + setCursor(clicked.copy(clicked.getAmount() - half)); //larger half + clicked.setAmount(half); + } else if (!cursor.isEmpty() && clicked.isEmpty()) { + cursor.sub(1); + setItem(action.slot, cursor.copy(1)); + } else if (InventoryUtils.canStack(cursor, clicked)) { + cursor.sub(1); + clicked.add(1); + } + break; + case DROP_ONE: + if (!clicked.isEmpty()) { + clicked.sub(1); + } + break; + case DROP_ALL: + setItem(action.slot, GeyserItemStack.EMPTY); + break; + } + } + } + + public Set getAffectedSlots() { + Set affectedSlots = new HashSet<>(); + for (ClickAction action : plan) { + if (translator.getSlotType(action.slot) == SlotType.NORMAL && action.slot != Click.OUTSIDE_SLOT) { + affectedSlots.add(action.slot); + } + } + return affectedSlots; + } + + @Value + private static class ClickAction { + Click click; + int slot; + } +} 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 index e45945bc..58e6178b 100644 --- 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 @@ -49,7 +49,7 @@ public class ChestInventoryUpdater extends InventoryUpdater { ItemData[] bedrockItems = new ItemData[paddedSize]; for (int i = 0; i < bedrockItems.length; i++) { if (i < translator.size) { - bedrockItems[i] = ItemTranslator.translateToBedrock(session, inventory.getItem(i)); + bedrockItems[i] = inventory.getItem(i).getItemData(session); } else { bedrockItems[i] = UNUSUABLE_SPACE_BLOCK; } @@ -69,7 +69,7 @@ public class ChestInventoryUpdater extends InventoryUpdater { InventorySlotPacket slotPacket = new InventorySlotPacket(); slotPacket.setContainerId(inventory.getId()); slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot)); - slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(javaSlot))); + slotPacket.setItem(inventory.getItem(javaSlot).getItemData(session)); session.sendUpstreamPacket(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 index 51f33ac4..1abc65b5 100644 --- 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 @@ -40,7 +40,7 @@ public class ContainerInventoryUpdater extends InventoryUpdater { ItemData[] bedrockItems = new ItemData[translator.size]; for (int i = 0; i < bedrockItems.length; i++) { - bedrockItems[translator.javaSlotToBedrock(i)] = ItemTranslator.translateToBedrock(session, inventory.getItem(i)); + bedrockItems[translator.javaSlotToBedrock(i)] = inventory.getItem(i).getItemData(session); } InventoryContentPacket contentPacket = new InventoryContentPacket(); @@ -57,7 +57,7 @@ public class ContainerInventoryUpdater extends InventoryUpdater { InventorySlotPacket slotPacket = new InventorySlotPacket(); slotPacket.setContainerId(inventory.getId()); slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot)); - slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(javaSlot))); + slotPacket.setItem(inventory.getItem(javaSlot).getItemData(session)); session.sendUpstreamPacket(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 index 57601a9e..655868c9 100644 --- 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 @@ -39,7 +39,7 @@ public abstract class InventoryUpdater { ItemData[] bedrockItems = new ItemData[36]; for (int i = 0; i < 36; i++) { final int offset = i < 9 ? 27 : -9; - bedrockItems[i] = ItemTranslator.translateToBedrock(session, inventory.getItem(translator.size + i + offset)); + bedrockItems[i] = inventory.getItem(translator.size + i + offset).getItemData(session); } InventoryContentPacket contentPacket = new InventoryContentPacket(); contentPacket.setContainerId(ContainerId.INVENTORY); @@ -52,7 +52,7 @@ public abstract class InventoryUpdater { InventorySlotPacket slotPacket = new InventorySlotPacket(); slotPacket.setContainerId(ContainerId.INVENTORY); slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot)); - slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(javaSlot))); + slotPacket.setItem(inventory.getItem(javaSlot).getItemData(session)); session.sendUpstreamPacket(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/UIInventoryUpdater.java similarity index 89% rename from connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/CursorInventoryUpdater.java rename to connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/UIInventoryUpdater.java index 26d88990..5100ddc9 100644 --- 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/UIInventoryUpdater.java @@ -32,9 +32,8 @@ import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.inventory.InventoryTranslator; import org.geysermc.connector.network.translators.item.ItemTranslator; -public class CursorInventoryUpdater extends InventoryUpdater { +public class UIInventoryUpdater extends InventoryUpdater { - //TODO: Consider renaming this? Since the Protocol enum updated @Override public void updateInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) { super.updateInventory(translator, session, inventory); @@ -46,7 +45,7 @@ public class CursorInventoryUpdater extends InventoryUpdater { InventorySlotPacket slotPacket = new InventorySlotPacket(); slotPacket.setContainerId(ContainerId.UI); slotPacket.setSlot(bedrockSlot); - slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(i))); + slotPacket.setItem(inventory.getItem(i).getItemData(session)); session.sendUpstreamPacket(slotPacket); } } @@ -59,7 +58,7 @@ public class CursorInventoryUpdater extends InventoryUpdater { InventorySlotPacket slotPacket = new InventorySlotPacket(); slotPacket.setContainerId(ContainerId.UI); slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot)); - slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(javaSlot))); + slotPacket.setItem(inventory.getItem(javaSlot).getItemData(session)); session.sendUpstreamPacket(slotPacket); return true; } 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 9ffb4f0d..559da5d9 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 @@ -34,6 +34,8 @@ import com.nukkitx.nbt.NbtMap; import com.nukkitx.protocol.bedrock.data.inventory.CraftingData; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import com.nukkitx.protocol.bedrock.packet.CraftingDataPacket; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntSet; import lombok.AllArgsConstructor; @@ -51,8 +53,10 @@ public class JavaDeclareRecipesTranslator extends PacketTranslator recipeMap = new Int2ObjectOpenHashMap<>(); CraftingDataPacket craftingDataPacket = new CraftingDataPacket(); craftingDataPacket.setCleanRecipes(true); + int netId = 1; for (Recipe recipe : packet.getRecipes()) { switch (recipe.getType()) { case CRAFTING_SHAPELESS: { @@ -63,7 +67,8 @@ public class JavaDeclareRecipesTranslator extends PacketTranslator // Max health must be divisible by two in bedrock entity.getAttributes().put(AttributeType.HEALTH, AttributeType.HEALTH.getAttribute(maxHealth, (maxHealth % 2 == 1 ? maxHealth + 1 : maxHealth))); - session.getInventoryCache().setOpenInventory(null); + session.setOpenInventory(null); SetPlayerGameTypePacket playerGameTypePacket = new SetPlayerGameTypePacket(); playerGameTypePacket.setGamemode(packet.getGamemode().ordinal()); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerActionAckTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerActionAckTranslator.java index 9ed11a23..05cc1a41 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerActionAckTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerActionAckTranslator.java @@ -32,6 +32,7 @@ import com.github.steveice10.opennbt.tag.builtin.*; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.LevelEventType; import com.nukkitx.protocol.bedrock.packet.LevelEventPacket; +import org.geysermc.connector.inventory.GeyserItemStack; import org.geysermc.connector.inventory.PlayerInventory; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; @@ -71,15 +72,9 @@ public class JavaPlayerActionAckTranslator extends PacketTranslator { + PlayerHotbarPacket hotbarPacket = new PlayerHotbarPacket(); + hotbarPacket.setContainerId(0); + hotbarPacket.setSelectedHotbarSlot(packet.getSlot()); + hotbarPacket.setSelectHotbarSlot(true); + session.sendUpstreamPacket(hotbarPacket); - session.getInventory().setHeldItemSlot(packet.getSlot()); + session.getPlayerInventory().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 93cfa08e..68f1dda9 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 @@ -26,6 +26,7 @@ package org.geysermc.connector.network.translators.java.window; import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerCloseWindowPacket; +import org.geysermc.connector.inventory.Inventory; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; @@ -36,7 +37,8 @@ public class JavaCloseWindowTranslator extends PacketTranslator { + InventoryUtils.closeInventory(session, packet.getWindowId()); + }); } } 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 e6787753..6d6c1686 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 @@ -27,6 +27,7 @@ package org.geysermc.connector.network.translators.java.window; import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientConfirmTransactionPacket; import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerConfirmTransactionPacket; +import org.geysermc.connector.inventory.Inventory; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; @@ -36,9 +37,11 @@ public class JavaConfirmTransactionTranslator extends PacketTranslator { + if (!packet.isAccepted()) { + ClientConfirmTransactionPacket confirmPacket = new ClientConfirmTransactionPacket(packet.getWindowId(), packet.getActionId(), true); + session.sendDownstreamPacket(confirmPacket); + } + }); } } 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 820639b3..e5ef4ecf 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 @@ -43,46 +43,47 @@ public class JavaOpenWindowTranslator extends PacketTranslator { + if (packet.getWindowId() == 0) { + return; + } + + InventoryTranslator newTranslator = InventoryTranslator.INVENTORY_TRANSLATORS.get(packet.getType()); + Inventory openInventory = session.getOpenInventory(); + //No translator exists for this window type. Close all windows and return. + if (newTranslator == null) { + if (openInventory != null) { + InventoryUtils.closeInventory(session, openInventory.getId()); + } + ClientCloseWindowPacket closeWindowPacket = new ClientCloseWindowPacket(packet.getWindowId()); + session.sendDownstreamPacket(closeWindowPacket); + return; + } + + String name = packet.getName(); + try { + JsonParser parser = new JsonParser(); + JsonObject jsonObject = parser.parse(packet.getName()).getAsJsonObject(); + if (jsonObject.has("text")) { + name = jsonObject.get("text").getAsString(); + } else if (jsonObject.has("translate")) { + name = jsonObject.get("translate").getAsString(); + } + } catch (Exception e) { + GeyserConnector.getInstance().getLogger().debug("JavaOpenWindowTranslator: " + e.toString()); + } + + name = LocaleUtils.getLocaleString(name, session.getClientData().getLanguageCode()); + + Inventory newInventory = newTranslator.createInventory(name, packet.getWindowId(), packet.getType(), session.getPlayerInventory()); if (openInventory != null) { - InventoryUtils.closeWindow(session, openInventory.getId()); - InventoryUtils.closeInventory(session, openInventory.getId()); + InventoryTranslator openTranslator = InventoryTranslator.INVENTORY_TRANSLATORS.get(openInventory.getWindowType()); + if (!openTranslator.getClass().equals(newTranslator.getClass())) { + InventoryUtils.closeInventory(session, openInventory.getId()); + } } - ClientCloseWindowPacket closeWindowPacket = new ClientCloseWindowPacket(packet.getWindowId()); - session.sendDownstreamPacket(closeWindowPacket); - return; - } - String name = packet.getName(); - try { - JsonParser parser = new JsonParser(); - JsonObject jsonObject = parser.parse(packet.getName()).getAsJsonObject(); - if (jsonObject.has("text")) { - name = jsonObject.get("text").getAsString(); - } else if (jsonObject.has("translate")) { - name = jsonObject.get("translate").getAsString(); - } - } catch (Exception e) { - GeyserConnector.getInstance().getLogger().debug("JavaOpenWindowTranslator: " + e.toString()); - } - - name = LocaleUtils.getLocaleString(name, session.getClientData().getLanguageCode()); - - Inventory newInventory = new Inventory(name, packet.getWindowId(), packet.getType(), newTranslator.size + 36); - session.getInventoryCache().cacheInventory(newInventory); - if (openInventory != null) { - InventoryTranslator openTranslator = InventoryTranslator.INVENTORY_TRANSLATORS.get(openInventory.getWindowType()); - if (!openTranslator.getClass().equals(newTranslator.getClass())) { - InventoryUtils.closeWindow(session, openInventory.getId()); - InventoryUtils.closeInventory(session, openInventory.getId()); - } - } - - InventoryUtils.openInventory(session, newInventory); + InventoryUtils.openInventory(session, newInventory); + }); } } 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 19d7db21..22a0edca 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 @@ -26,37 +26,53 @@ package org.geysermc.connector.network.translators.java.window; import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerSetSlotPacket; +import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import org.geysermc.connector.inventory.GeyserItemStack; import org.geysermc.connector.inventory.Inventory; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; -import org.geysermc.connector.network.translators.inventory.InventoryTranslator; import org.geysermc.connector.network.translators.Translator; +import org.geysermc.connector.network.translators.inventory.InventoryTranslator; import org.geysermc.connector.utils.InventoryUtils; -import java.util.Objects; - @Translator(packet = ServerSetSlotPacket.class) public class JavaSetSlotTranslator extends PacketTranslator { @Override public void translate(ServerSetSlotPacket packet, GeyserSession session) { - if (packet.getWindowId() == 255 && packet.getSlot() == -1) { //cursor - if (session.getCraftSlot() != 0) + session.addInventoryTask(() -> { + if (packet.getWindowId() == 255) { //cursor + GeyserItemStack newItem = GeyserItemStack.from(packet.getItem()); + GeyserItemStack oldItem = session.getPlayerInventory().getCursor(); + if (newItem.getItemData(session).equals(oldItem.getItemData(session))) { + newItem.setNetId(oldItem.getNetId()); + } else { + newItem.setNetId(session.getItemNetId().getAndIncrement()); + } + session.getPlayerInventory().setCursor(newItem); + InventoryUtils.updateCursor(session); + return; + } + + //TODO: support window id -2, should update player inventory + Inventory inventory = InventoryUtils.getInventory(session, packet.getWindowId()); + if (inventory == null) return; - session.getInventory().setCursor(packet.getItem()); - InventoryUtils.updateCursor(session); - return; - } - - Inventory inventory = session.getInventoryCache().getInventories().get(packet.getWindowId()); - if (inventory == null || (packet.getWindowId() != 0 && inventory.getWindowType() == null)) - return; - - InventoryTranslator translator = InventoryTranslator.INVENTORY_TRANSLATORS.get(inventory.getWindowType()); - if (translator != null) { - inventory.setItem(packet.getSlot(), packet.getItem()); - translator.updateSlot(session, inventory, packet.getSlot()); - } + InventoryTranslator translator = InventoryTranslator.INVENTORY_TRANSLATORS.get(inventory.getWindowType()); + if (translator != null) { + GeyserItemStack newItem = GeyserItemStack.from(packet.getItem()); + GeyserItemStack oldItem = inventory.getItem(packet.getSlot()); + if (newItem.getItemData(session).equals(oldItem.getItemData(session), false, false, false)) { + newItem.setNetId(oldItem.getNetId()); + System.out.println("OLD: " + newItem.getNetId()); + } else { + newItem.setNetId(session.getItemNetId().getAndIncrement()); + System.out.println("NEW: " + newItem.getNetId()); + } + inventory.setItem(packet.getSlot(), newItem); + translator.updateSlot(session, inventory, packet.getSlot()); + } + }); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaWindowItemsTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaWindowItemsTranslator.java index 2cc392f5..26aab47b 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaWindowItemsTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaWindowItemsTranslator.java @@ -26,32 +26,40 @@ package org.geysermc.connector.network.translators.java.window; import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerWindowItemsPacket; +import com.nukkitx.protocol.bedrock.data.inventory.ItemData; +import org.geysermc.connector.inventory.GeyserItemStack; import org.geysermc.connector.inventory.Inventory; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; import org.geysermc.connector.network.translators.inventory.InventoryTranslator; - -import java.util.Arrays; +import org.geysermc.connector.utils.InventoryUtils; @Translator(packet = ServerWindowItemsPacket.class) public class JavaWindowItemsTranslator extends PacketTranslator { @Override public void translate(ServerWindowItemsPacket packet, GeyserSession session) { - Inventory inventory = session.getInventoryCache().getInventories().get(packet.getWindowId()); - if (inventory == null || (packet.getWindowId() != 0 && inventory.getWindowType() == null)) - return; + session.addInventoryTask(() -> { + Inventory inventory = InventoryUtils.getInventory(session, packet.getWindowId()); + if (inventory == null) + return; - if (packet.getItems().length < inventory.getSize()) { - inventory.setItems(Arrays.copyOf(packet.getItems(), inventory.getSize())); - } else { - inventory.setItems(packet.getItems()); - } + for (int i = 0; i < packet.getItems().length; i++) { + GeyserItemStack newItem = GeyserItemStack.from(packet.getItems()[i]); + GeyserItemStack oldItem = inventory.getItem(i); + if (newItem.getItemData(session).equals(oldItem.getItemData(session), false, false, false)) { + newItem.setNetId(oldItem.getNetId()); + } else { + newItem.setNetId(session.getItemNetId().getAndIncrement()); + } + inventory.setItem(i, newItem); + } - InventoryTranslator translator = InventoryTranslator.INVENTORY_TRANSLATORS.get(inventory.getWindowType()); - if (translator != null) { - translator.updateInventory(session, inventory); - } + InventoryTranslator translator = InventoryTranslator.INVENTORY_TRANSLATORS.get(inventory.getWindowType()); + if (translator != null) { + translator.updateInventory(session, inventory); + } + }); } } 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 daebed1b..97c4708f 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 @@ -31,19 +31,22 @@ import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; import org.geysermc.connector.network.translators.inventory.InventoryTranslator; +import org.geysermc.connector.utils.InventoryUtils; @Translator(packet = ServerWindowPropertyPacket.class) public class JavaWindowPropertyTranslator extends PacketTranslator { @Override public void translate(ServerWindowPropertyPacket packet, GeyserSession session) { - Inventory inventory = session.getInventoryCache().getInventories().get(packet.getWindowId()); - if (inventory == null || (packet.getWindowId() != 0 && inventory.getWindowType() == null)) - return; + session.addInventoryTask(() -> { + Inventory inventory = InventoryUtils.getInventory(session, packet.getWindowId()); + if (inventory == null) + return; - InventoryTranslator translator = InventoryTranslator.INVENTORY_TRANSLATORS.get(inventory.getWindowType()); - if (translator != null) { - translator.updateProperty(session, inventory, packet.getRawProperty(), packet.getValue()); - } + InventoryTranslator translator = InventoryTranslator.INVENTORY_TRANSLATORS.get(inventory.getWindowType()); + if (translator != null) { + translator.updateProperty(session, inventory, packet.getRawProperty(), packet.getValue()); + } + }); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java index d7961dd9..cb1aeaa7 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java @@ -111,7 +111,7 @@ public class JavaNotifyClientTranslator extends PacketTranslator tags = new ArrayList<>(); - for (VillagerTrade trade : packet.getTrades()) { + for (int i = 0; i < packet.getTrades().length; i++) { + VillagerTrade trade = packet.getTrades()[i]; NbtMapBuilder recipe = NbtMap.builder(); - recipe.putInt("maxUses", trade.getMaxUses()); + recipe.putInt("netId", i + 1); + recipe.putInt("maxUses", trade.isTradeDisabled() ? 0 : trade.getMaxUses()); recipe.putInt("traderExp", trade.getXp()); recipe.putFloat("priceMultiplierA", trade.getPriceMultiplier()); recipe.put("sell", getItemTag(session, trade.getOutput(), 0)); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/sound/BlockSoundInteractionHandler.java b/connector/src/main/java/org/geysermc/connector/network/translators/sound/BlockSoundInteractionHandler.java index f3dff0cc..ab73e602 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/sound/BlockSoundInteractionHandler.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/sound/BlockSoundInteractionHandler.java @@ -27,6 +27,7 @@ package org.geysermc.connector.network.translators.sound; import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; import com.nukkitx.math.vector.Vector3f; +import org.geysermc.connector.inventory.GeyserItemStack; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.item.ItemRegistry; @@ -60,12 +61,12 @@ public interface BlockSoundInteractionHandler extends SoundInteractionHandler 500) { - ContainerClosePacket closePacket = new ContainerClosePacket(); - closePacket.setId((byte) windowId); - session.sendUpstreamPacket(closePacket); + Inventory inventory = getInventory(session, windowId); + if (inventory != null) { + InventoryTranslator translator = InventoryTranslator.INVENTORY_TRANSLATORS.get(inventory.getWindowType()); + translator.closeInventory(session, inventory); session.setLastWindowCloseTime(System.currentTimeMillis()); } - */ + session.setOpenInventory(null); + } + + public static Inventory getInventory(GeyserSession session, int windowId) { + if (windowId == 0) { + return session.getPlayerInventory(); + } else { + Inventory openInventory = session.getOpenInventory(); + if (openInventory != null && windowId == openInventory.getId()) { + return openInventory; + } + return null; + } } public static void updateCursor(GeyserSession session) { InventorySlotPacket cursorPacket = new InventorySlotPacket(); cursorPacket.setContainerId(ContainerId.UI); cursorPacket.setSlot(0); - cursorPacket.setItem(ItemTranslator.translateToBedrock(session, session.getInventory().getCursor())); + cursorPacket.setItem(session.getPlayerInventory().getCursor().getItemData(session)); session.sendUpstreamPacket(cursorPacket); } + public static boolean canStack(GeyserItemStack item1, GeyserItemStack item2) { + if (item1.isEmpty() || item2.isEmpty()) + return false; + return item1.getId() == item2.getId() && Objects.equals(item1.getNbt(), item2.getNbt()); + } + public static boolean canStack(ItemStack item1, ItemStack item2) { if (item1 == null || item2 == null) return false; @@ -170,19 +162,16 @@ public class InventoryUtils { */ public static void findOrCreatePickedBlock(GeyserSession session, String itemName) { // Get the inventory to choose a slot to pick - Inventory inventory = session.getInventoryCache().getOpenInventory(); - if (inventory == null) { - inventory = session.getInventory(); - } + PlayerInventory inventory = session.getPlayerInventory(); // Check hotbar for item for (int i = 36; i < 45; i++) { - if (inventory.getItem(i) == null) { + GeyserItemStack geyserItem = inventory.getItem(i); + if (geyserItem.isEmpty()) { continue; } - ItemEntry item = ItemRegistry.getItem(inventory.getItem(i)); // If this isn't the item we're looking for - if (!item.getJavaIdentifier().equals(itemName)) { + if (!geyserItem.getItemEntry().getJavaIdentifier().equals(itemName)) { continue; } @@ -193,12 +182,12 @@ public class InventoryUtils { // Check inventory for item for (int i = 9; i < 36; i++) { - if (inventory.getItem(i) == null) { + GeyserItemStack geyserItem = inventory.getItem(i); + if (geyserItem.isEmpty()) { continue; } - ItemEntry item = ItemRegistry.getItem(inventory.getItem(i)); // If this isn't the item we're looking for - if (!item.getJavaIdentifier().equals(itemName)) { + if (!geyserItem.getItemEntry().getJavaIdentifier().equals(itemName)) { continue; } @@ -209,10 +198,10 @@ public class InventoryUtils { // If we still have not found the item, and we're in creative, ask for the item from the server. if (session.getGameMode() == GameMode.CREATIVE) { - int slot = session.getInventory().getHeldItemSlot() + 36; - if (session.getInventory().getItemInHand() != null) { // Otherwise we should just use the current slot + int slot = inventory.getHeldItemSlot() + 36; + if (!inventory.getItemInHand().isEmpty()) { // Otherwise we should just use the current slot for (int i = 36; i < 45; i++) { - if (inventory.getItem(i) == null) { + if (inventory.getItem(i).isEmpty()) { slot = i; break; } @@ -221,7 +210,7 @@ public class InventoryUtils { ClientCreativeInventoryActionPacket actionPacket = new ClientCreativeInventoryActionPacket(slot, new ItemStack(ItemRegistry.getItemEntry(itemName).getJavaId())); - if ((slot - 36) != session.getInventory().getHeldItemSlot()) { + if ((slot - 36) != inventory.getHeldItemSlot()) { setHotbarItem(session, slot); } session.sendDownstreamPacket(actionPacket); diff --git a/connector/src/main/resources/languages b/connector/src/main/resources/languages index 5f217922..93b2caed 160000 --- a/connector/src/main/resources/languages +++ b/connector/src/main/resources/languages @@ -1 +1 @@ -Subproject commit 5f21792264a364e32425014e0be79db93593da1e +Subproject commit 93b2caed3c4ecd94b3c77a87f1b2304a7bf4f062 diff --git a/connector/src/main/resources/mappings b/connector/src/main/resources/mappings index 0fae8d3f..ec8b6829 160000 --- a/connector/src/main/resources/mappings +++ b/connector/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit 0fae8d3f0de6210a10435a36128db14cb7650ae6 +Subproject commit ec8b68297c4d62a9e4d640a8c7e77977062628ee From 3b3e72d5c3e37e3c0747d63f67ddafef48936733 Mon Sep 17 00:00:00 2001 From: DoctorMacc Date: Tue, 15 Dec 2020 23:01:27 -0500 Subject: [PATCH 02/39] Initial crafting table support --- .../BedrockItemStackRequestTranslator.java | 1 - .../CraftingInventoryTranslator.java | 114 ++++++++++++++++++ .../inventory/InventoryTranslator.java | 4 +- .../holder/BlockInventoryHolder.java | 3 + 4 files changed, 119 insertions(+), 3 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/CraftingInventoryTranslator.java diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockItemStackRequestTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockItemStackRequestTranslator.java index 00ca83c7..8190bb55 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockItemStackRequestTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockItemStackRequestTranslator.java @@ -32,7 +32,6 @@ import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; import org.geysermc.connector.network.translators.inventory.InventoryTranslator; -import org.geysermc.connector.utils.InventoryUtils; @Translator(packet = ItemStackRequestPacket.class) public class BedrockItemStackRequestTranslator extends PacketTranslator { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/CraftingInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/CraftingInventoryTranslator.java new file mode 100644 index 00000000..61dd9b04 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/CraftingInventoryTranslator.java @@ -0,0 +1,114 @@ +/* + * 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; + +import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; +import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; +import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData; +import org.geysermc.connector.inventory.Inventory; +import org.geysermc.connector.network.session.GeyserSession; +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.ContainerInventoryUpdater; +import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater; +import org.geysermc.connector.network.translators.world.block.BlockTranslator; + +public class CraftingInventoryTranslator extends BaseInventoryTranslator { + private final InventoryHolder holder; + private final InventoryUpdater updater; + + CraftingInventoryTranslator() { + super(10); + int javaBlockState = BlockTranslator.getJavaBlockState("minecraft:crafting_table"); + this.holder = new BlockInventoryHolder(BlockTranslator.getBedrockBlockId(javaBlockState), ContainerType.WORKBENCH); + this.updater = new ContainerInventoryUpdater(); + } + + @Override + public void prepareInventory(GeyserSession session, Inventory inventory) { + holder.prepareInventory(this, session, inventory); + } + + @Override + public void openInventory(GeyserSession session, Inventory inventory) { + holder.openInventory(this, session, inventory); + } + + @Override + public void closeInventory(GeyserSession session, Inventory inventory) { + 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); + } + + @Override + public SlotType getSlotType(int javaSlot) { + if (javaSlot == 0) + return SlotType.OUTPUT; + return SlotType.NORMAL; + } + + + @Override + public int javaSlotToBedrock(int slot) { + if (slot < size) { + return slot == 0 ? 50 : slot + 31; + } + return super.javaSlotToBedrock(slot); + } + + @Override + public BedrockContainerSlot javaSlotToBedrockContainer(int slot) { + if (slot >= 1 && slot <= 9) { + return new BedrockContainerSlot(ContainerSlotType.CRAFTING_INPUT, slot + 31); + } + if (slot == 0) { + return new BedrockContainerSlot(ContainerSlotType.CRAFTING_OUTPUT, 0); + } + return super.javaSlotToBedrockContainer(slot); + } + + @Override + public int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData) { + if (slotInfoData.getContainer() == ContainerSlotType.CRAFTING_INPUT) { + // Java goes from 1 - 9, left to right then up to down + // Bedrock is the same, but it starts from 32. + return slotInfoData.getSlot() - 31; + } + if (slotInfoData.getContainer() == ContainerSlotType.CRAFTING_OUTPUT || slotInfoData.getContainer() == ContainerSlotType.CREATIVE_OUTPUT) { + return 0; + } + return super.bedrockSlotToJava(slotInfoData); + } +} 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 69670a3d..4fa54b38 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 @@ -58,9 +58,9 @@ public abstract class InventoryTranslator { put(WindowType.GENERIC_9X4, new DoubleChestInventoryTranslator(36)); put(WindowType.GENERIC_9X5, new DoubleChestInventoryTranslator(45)); put(WindowType.GENERIC_9X6, new DoubleChestInventoryTranslator(54)); + put(WindowType.CRAFTING, new CraftingInventoryTranslator()); /*put(WindowType.BREWING_STAND, new BrewingInventoryTranslator()); put(WindowType.ANVIL, new AnvilInventoryTranslator()); - put(WindowType.CRAFTING, new CraftingInventoryTranslator()); put(WindowType.GRINDSTONE, new GrindstoneInventoryTranslator());*/ put(WindowType.MERCHANT, new MerchantInventoryTranslator()); /*put(WindowType.SMITHING, new SmithingInventoryTranslator()); @@ -295,7 +295,7 @@ public abstract class InventoryTranslator { } craftState = CraftState.RECIPE_ID; recipeId = craftAction.getRecipeNetworkId(); - System.out.println(session.getCraftingRecipes().get(recipeId).toString()); + System.out.println(session.getCraftingRecipes().get(recipeId)); autoCraft = false; break; } 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 index 6afdb25d..339c8a54 100644 --- 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 @@ -38,6 +38,9 @@ import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.inventory.InventoryTranslator; import org.geysermc.connector.network.translators.world.block.BlockTranslator; +/** + * Manages the fake block we implement for each inventory. + */ @AllArgsConstructor public class BlockInventoryHolder extends InventoryHolder { private final int blockId; From 988e697a706ba1cbc5617a60b17c209d93395959 Mon Sep 17 00:00:00 2001 From: DoctorMacc Date: Tue, 15 Dec 2020 23:33:17 -0500 Subject: [PATCH 03/39] Crafting table works --- .../CraftingInventoryTranslator.java | 4 +- .../inventory/InventoryTranslator.java | 31 +++++---- .../updater/CursorInventoryUpdater.java | 64 +++++++++++++++++++ 3 files changed, 85 insertions(+), 14 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/CursorInventoryUpdater.java diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/CraftingInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/CraftingInventoryTranslator.java index 61dd9b04..a266074d 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/CraftingInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/CraftingInventoryTranslator.java @@ -32,7 +32,7 @@ import org.geysermc.connector.inventory.Inventory; import org.geysermc.connector.network.session.GeyserSession; 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.ContainerInventoryUpdater; +import org.geysermc.connector.network.translators.inventory.updater.CursorInventoryUpdater; import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater; import org.geysermc.connector.network.translators.world.block.BlockTranslator; @@ -44,7 +44,7 @@ public class CraftingInventoryTranslator extends BaseInventoryTranslator { super(10); int javaBlockState = BlockTranslator.getJavaBlockState("minecraft:crafting_table"); this.holder = new BlockInventoryHolder(BlockTranslator.getBedrockBlockId(javaBlockState), ContainerType.WORKBENCH); - this.updater = new ContainerInventoryUpdater(); + this.updater = new CursorInventoryUpdater(); } @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 4fa54b38..a9ac58cb 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 @@ -295,21 +295,28 @@ public abstract class InventoryTranslator { } craftState = CraftState.RECIPE_ID; recipeId = craftAction.getRecipeNetworkId(); - System.out.println(session.getCraftingRecipes().get(recipeId)); + //System.out.println(session.getCraftingRecipes().get(recipeId)); autoCraft = false; break; } - /*case CRAFT_RECIPE_AUTO: { - AutoCraftRecipeStackRequestActionData autoCraftAction = (AutoCraftRecipeStackRequestActionData) action; - if (craftState != CraftState.START) { - return rejectRequest(request); - } - craftState = CraftState.RECIPE_ID; - recipeId = autoCraftAction.getRecipeNetworkId(); - autoCraft = true; - //TODO: reject transaction if crafting grid is not clear - break; - }*/ +// case CRAFT_RECIPE_AUTO: { +// AutoCraftRecipeStackRequestActionData autoCraftAction = (AutoCraftRecipeStackRequestActionData) action; +// if (craftState != CraftState.START) { +// return rejectRequest(request); +// } +// craftState = CraftState.RECIPE_ID; +// recipeId = autoCraftAction.getRecipeNetworkId(); +// Recipe recipe = session.getCraftingRecipes().get(recipeId); +// System.out.println(recipe); +// if (recipe == null) { +// return rejectRequest(request); +// } +//// ClientPrepareCraftingGridPacket packet = new ClientPrepareCraftingGridPacket(session.getOpenInventory().getId(), recipe.getIdentifier(), true); +//// session.sendDownstreamPacket(packet); +// autoCraft = true; +// //TODO: reject transaction if crafting grid is not clear +// break; +// } case CRAFT_RESULTS_DEPRECATED: { CraftResultsDeprecatedStackRequestActionData deprecatedCraftAction = (CraftResultsDeprecatedStackRequestActionData) action; if (craftState != CraftState.RECIPE_ID) { 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 00000000..43306ea1 --- /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.inventory.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.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.UI); + slotPacket.setSlot(bedrockSlot); + slotPacket.setItem(inventory.getItem(i).getItemData(session)); + session.sendUpstreamPacket(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.UI); + slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot)); + slotPacket.setItem(inventory.getItem(javaSlot).getItemData(session)); + session.sendUpstreamPacket(slotPacket); + return true; + } +} From 929b0ba80cc9d128c51826f1f459232f4853af36 Mon Sep 17 00:00:00 2001 From: DoctorMacc Date: Wed, 16 Dec 2020 23:52:45 -0500 Subject: [PATCH 04/39] Refactors, and add furnace --- .../inventory/InventoryTranslator.java | 27 ++++--- .../AbstractBlockInventoryTranslator.java | 80 ++++++++++++++++++ .../BaseInventoryTranslator.java | 7 +- .../CraftingInventoryTranslator.java | 56 ++----------- .../MerchantInventoryTranslator.java | 4 +- .../PlayerInventoryTranslator.java | 5 +- .../chest}/ChestInventoryTranslator.java | 4 +- .../DoubleChestInventoryTranslator.java | 2 +- .../SingleChestInventoryTranslator.java | 2 +- .../AbstractFurnaceInventoryTranslator.java | 81 +++++++++++++++++++ .../BlastFurnaceInventoryTranslator.java | 44 ++++++++++ .../furnace/FurnaceInventoryTranslator.java | 44 ++++++++++ .../furnace/SmokerInventoryTranslator.java | 44 ++++++++++ .../world/JavaNotifyClientTranslator.java | 2 +- .../connector/utils/InventoryUtils.java | 4 +- 15 files changed, 336 insertions(+), 70 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/AbstractBlockInventoryTranslator.java rename connector/src/main/java/org/geysermc/connector/network/translators/inventory/{ => translators}/BaseInventoryTranslator.java (90%) rename connector/src/main/java/org/geysermc/connector/network/translators/inventory/{ => translators}/CraftingInventoryTranslator.java (57%) rename connector/src/main/java/org/geysermc/connector/network/translators/inventory/{ => translators}/MerchantInventoryTranslator.java (96%) rename connector/src/main/java/org/geysermc/connector/network/translators/inventory/{ => translators}/PlayerInventoryTranslator.java (96%) rename connector/src/main/java/org/geysermc/connector/network/translators/inventory/{ => translators/chest}/ChestInventoryTranslator.java (90%) rename connector/src/main/java/org/geysermc/connector/network/translators/inventory/{ => translators/chest}/DoubleChestInventoryTranslator.java (98%) rename connector/src/main/java/org/geysermc/connector/network/translators/inventory/{ => translators/chest}/SingleChestInventoryTranslator.java (96%) create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/furnace/AbstractFurnaceInventoryTranslator.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/furnace/BlastFurnaceInventoryTranslator.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/furnace/FurnaceInventoryTranslator.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/furnace/SmokerInventoryTranslator.java 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 a9ac58cb..a0c1a3fa 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 @@ -40,6 +40,16 @@ import org.geysermc.connector.inventory.PlayerInventory; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.inventory.click.Click; import org.geysermc.connector.network.translators.inventory.click.ClickPlan; +import org.geysermc.connector.network.translators.inventory.translators.CraftingInventoryTranslator; +import org.geysermc.connector.network.translators.inventory.translators.MerchantInventoryTranslator; +import org.geysermc.connector.network.translators.inventory.translators.PlayerInventoryTranslator; +import org.geysermc.connector.network.translators.inventory.translators.chest.DoubleChestInventoryTranslator; +import org.geysermc.connector.network.translators.inventory.translators.chest.SingleChestInventoryTranslator; +import org.geysermc.connector.network.translators.inventory.translators.furnace.BlastFurnaceInventoryTranslator; +import org.geysermc.connector.network.translators.inventory.translators.furnace.FurnaceInventoryTranslator; +import org.geysermc.connector.network.translators.inventory.translators.furnace.SmokerInventoryTranslator; +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.ItemRegistry; import org.geysermc.connector.network.translators.item.ItemTranslator; import org.geysermc.connector.utils.InventoryUtils; @@ -63,19 +73,18 @@ public abstract class InventoryTranslator { put(WindowType.ANVIL, new AnvilInventoryTranslator()); put(WindowType.GRINDSTONE, new GrindstoneInventoryTranslator());*/ put(WindowType.MERCHANT, new MerchantInventoryTranslator()); - /*put(WindowType.SMITHING, new SmithingInventoryTranslator()); + //put(WindowType.SMITHING, new SmithingInventoryTranslator()); //put(WindowType.ENCHANTMENT, new EnchantmentInventoryTranslator()); //TODO - InventoryTranslator furnace = new FurnaceInventoryTranslator(); - put(WindowType.FURNACE, furnace); - put(WindowType.BLAST_FURNACE, furnace); - put(WindowType.SMOKER, furnace); + put(WindowType.FURNACE, new FurnaceInventoryTranslator()); + put(WindowType.BLAST_FURNACE, new BlastFurnaceInventoryTranslator()); + put(WindowType.SMOKER, new SmokerInventoryTranslator()); InventoryUpdater containerUpdater = new ContainerInventoryUpdater(); - put(WindowType.GENERIC_3X3, new BlockInventoryTranslator(9, "minecraft:dispenser[facing=north,triggered=false]", ContainerType.DISPENSER, containerUpdater)); - put(WindowType.HOPPER, new BlockInventoryTranslator(5, "minecraft:hopper[enabled=false,facing=down]", ContainerType.HOPPER, containerUpdater)); - put(WindowType.SHULKER_BOX, new BlockInventoryTranslator(27, "minecraft:shulker_box[facing=north]", ContainerType.CONTAINER, containerUpdater)); - //put(WindowType.BEACON, new BlockInventoryTranslator(1, "minecraft:beacon", ContainerType.BEACON)); //TODO*/ + //put(WindowType.GENERIC_3X3, new AbstractBlockInventoryTranslator(9, "minecraft:dispenser[facing=north,triggered=false]", ContainerType.DISPENSER, containerUpdater)); + //put(WindowType.HOPPER, new AbstractBlockInventoryTranslator(5, "minecraft:hopper[enabled=false,facing=down]", ContainerType.HOPPER, containerUpdater)); + //put(WindowType.SHULKER_BOX, new AbstractBlockInventoryTranslator(27, "minecraft:shulker_box[facing=north]", ContainerType.CONTAINER, containerUpdater)); + //put(WindowType.BEACON, new AbstractBlockInventoryTranslator(1, "minecraft:beacon", ContainerType.BEACON)); //TODO*/ } }; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/AbstractBlockInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/AbstractBlockInventoryTranslator.java new file mode 100644 index 00000000..bff90c52 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/AbstractBlockInventoryTranslator.java @@ -0,0 +1,80 @@ +/* + * 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.translators; + +import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; +import org.geysermc.connector.inventory.Inventory; +import org.geysermc.connector.network.session.GeyserSession; +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; +import org.geysermc.connector.network.translators.world.block.BlockTranslator; + +/** + * Provided as a base for any inventory that requires a block for opening it + */ +public abstract class AbstractBlockInventoryTranslator extends BaseInventoryTranslator { + private final InventoryHolder holder; + private final InventoryUpdater updater; + + /** + * @param size the amount of slots that the inventory adds alongside the base inventory slots + * @param javaBlockIdentifier a Java block identifier that is used as a temporary block + * @param containerType the container type of this inventory + * @param updater updater + */ + public AbstractBlockInventoryTranslator(int size, String javaBlockIdentifier, ContainerType containerType, InventoryUpdater updater) { + super(size); + int javaBlockState = BlockTranslator.getJavaBlockState(javaBlockIdentifier); + this.holder = new BlockInventoryHolder(BlockTranslator.getBedrockBlockId(javaBlockState), containerType); + this.updater = updater; + } + + @Override + public void prepareInventory(GeyserSession session, Inventory inventory) { + holder.prepareInventory(this, session, inventory); + } + + @Override + public void openInventory(GeyserSession session, Inventory inventory) { + holder.openInventory(this, session, inventory); + } + + @Override + public void closeInventory(GeyserSession session, Inventory inventory) { + 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/BaseInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/BaseInventoryTranslator.java similarity index 90% rename from connector/src/main/java/org/geysermc/connector/network/translators/inventory/BaseInventoryTranslator.java rename to connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/BaseInventoryTranslator.java index 7d54667b..48d2ac10 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/BaseInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/BaseInventoryTranslator.java @@ -23,7 +23,7 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.connector.network.translators.inventory; +package org.geysermc.connector.network.translators.inventory.translators; import com.github.steveice10.mc.protocol.data.game.window.WindowType; import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; @@ -32,9 +32,12 @@ import org.geysermc.connector.inventory.Container; 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.BedrockContainerSlot; +import org.geysermc.connector.network.translators.inventory.InventoryTranslator; +import org.geysermc.connector.network.translators.inventory.SlotType; public abstract class BaseInventoryTranslator extends InventoryTranslator { - BaseInventoryTranslator(int size) { + public BaseInventoryTranslator(int size) { super(size); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/CraftingInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/CraftingInventoryTranslator.java similarity index 57% rename from connector/src/main/java/org/geysermc/connector/network/translators/inventory/CraftingInventoryTranslator.java rename to connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/CraftingInventoryTranslator.java index a266074d..2c420ada 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/CraftingInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/CraftingInventoryTranslator.java @@ -23,53 +23,18 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.connector.network.translators.inventory; +package org.geysermc.connector.network.translators.inventory.translators; import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData; -import org.geysermc.connector.inventory.Inventory; -import org.geysermc.connector.network.session.GeyserSession; -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.BedrockContainerSlot; +import org.geysermc.connector.network.translators.inventory.SlotType; import org.geysermc.connector.network.translators.inventory.updater.CursorInventoryUpdater; -import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater; -import org.geysermc.connector.network.translators.world.block.BlockTranslator; -public class CraftingInventoryTranslator extends BaseInventoryTranslator { - private final InventoryHolder holder; - private final InventoryUpdater updater; - - CraftingInventoryTranslator() { - super(10); - int javaBlockState = BlockTranslator.getJavaBlockState("minecraft:crafting_table"); - this.holder = new BlockInventoryHolder(BlockTranslator.getBedrockBlockId(javaBlockState), ContainerType.WORKBENCH); - this.updater = new CursorInventoryUpdater(); - } - - @Override - public void prepareInventory(GeyserSession session, Inventory inventory) { - holder.prepareInventory(this, session, inventory); - } - - @Override - public void openInventory(GeyserSession session, Inventory inventory) { - holder.openInventory(this, session, inventory); - } - - @Override - public void closeInventory(GeyserSession session, Inventory inventory) { - 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); +public class CraftingInventoryTranslator extends AbstractBlockInventoryTranslator { + public CraftingInventoryTranslator() { + super(10, "minecraft:crafting_table", ContainerType.WORKBENCH, new CursorInventoryUpdater()); } @Override @@ -79,15 +44,6 @@ public class CraftingInventoryTranslator extends BaseInventoryTranslator { return SlotType.NORMAL; } - - @Override - public int javaSlotToBedrock(int slot) { - if (slot < size) { - return slot == 0 ? 50 : slot + 31; - } - return super.javaSlotToBedrock(slot); - } - @Override public BedrockContainerSlot javaSlotToBedrockContainer(int slot) { if (slot >= 1 && slot <= 9) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/MerchantInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/MerchantInventoryTranslator.java similarity index 96% rename from connector/src/main/java/org/geysermc/connector/network/translators/inventory/MerchantInventoryTranslator.java rename to connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/MerchantInventoryTranslator.java index c4b2fc9a..e798157d 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/MerchantInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/MerchantInventoryTranslator.java @@ -23,7 +23,7 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.connector.network.translators.inventory; +package org.geysermc.connector.network.translators.inventory.translators; import com.github.steveice10.mc.protocol.data.game.window.WindowType; import com.nukkitx.math.vector.Vector3f; @@ -38,6 +38,8 @@ import org.geysermc.connector.inventory.Inventory; import org.geysermc.connector.inventory.MerchantContainer; import org.geysermc.connector.inventory.PlayerInventory; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.inventory.BedrockContainerSlot; +import org.geysermc.connector.network.translators.inventory.SlotType; import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater; import org.geysermc.connector.network.translators.inventory.updater.UIInventoryUpdater; 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/translators/PlayerInventoryTranslator.java similarity index 96% rename from connector/src/main/java/org/geysermc/connector/network/translators/inventory/PlayerInventoryTranslator.java rename to connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/PlayerInventoryTranslator.java index 5ace8fde..286c3f63 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/translators/PlayerInventoryTranslator.java @@ -23,7 +23,7 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.connector.network.translators.inventory; +package org.geysermc.connector.network.translators.inventory.translators; import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; import com.github.steveice10.mc.protocol.data.game.window.WindowType; @@ -34,6 +34,9 @@ import com.nukkitx.protocol.bedrock.packet.ItemStackRequestPacket; 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.BedrockContainerSlot; +import org.geysermc.connector.network.translators.inventory.InventoryTranslator; +import org.geysermc.connector.network.translators.inventory.SlotType; import org.geysermc.connector.network.translators.item.ItemTranslator; import org.geysermc.connector.utils.InventoryUtils; import org.geysermc.connector.utils.LanguageUtils; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/ChestInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/chest/ChestInventoryTranslator.java similarity index 90% rename from connector/src/main/java/org/geysermc/connector/network/translators/inventory/ChestInventoryTranslator.java rename to connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/chest/ChestInventoryTranslator.java index df97563e..1e81bb67 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/ChestInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/chest/ChestInventoryTranslator.java @@ -23,11 +23,13 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.connector.network.translators.inventory; +package org.geysermc.connector.network.translators.inventory.translators.chest; import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; import org.geysermc.connector.inventory.Inventory; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.inventory.BedrockContainerSlot; +import org.geysermc.connector.network.translators.inventory.translators.BaseInventoryTranslator; import org.geysermc.connector.network.translators.inventory.updater.ChestInventoryUpdater; import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater; 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/translators/chest/DoubleChestInventoryTranslator.java similarity index 98% rename from connector/src/main/java/org/geysermc/connector/network/translators/inventory/DoubleChestInventoryTranslator.java rename to connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/chest/DoubleChestInventoryTranslator.java index 1183b21d..f807cfe0 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/translators/chest/DoubleChestInventoryTranslator.java @@ -23,7 +23,7 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.connector.network.translators.inventory; +package org.geysermc.connector.network.translators.inventory.translators.chest; import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; import com.nukkitx.math.vector.Vector3i; 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/translators/chest/SingleChestInventoryTranslator.java similarity index 96% rename from connector/src/main/java/org/geysermc/connector/network/translators/inventory/SingleChestInventoryTranslator.java rename to connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/chest/SingleChestInventoryTranslator.java index 45860dcd..43e06006 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/translators/chest/SingleChestInventoryTranslator.java @@ -23,7 +23,7 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.connector.network.translators.inventory; +package org.geysermc.connector.network.translators.inventory.translators.chest; import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; import org.geysermc.connector.inventory.Inventory; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/furnace/AbstractFurnaceInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/furnace/AbstractFurnaceInventoryTranslator.java new file mode 100644 index 00000000..b5f5b97f --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/furnace/AbstractFurnaceInventoryTranslator.java @@ -0,0 +1,81 @@ +/* + * 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.translators.furnace; + +import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; +import com.nukkitx.protocol.bedrock.data.inventory.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.BedrockContainerSlot; +import org.geysermc.connector.network.translators.inventory.SlotType; +import org.geysermc.connector.network.translators.inventory.translators.AbstractBlockInventoryTranslator; +import org.geysermc.connector.network.translators.inventory.updater.ContainerInventoryUpdater; + +public abstract class AbstractFurnaceInventoryTranslator extends AbstractBlockInventoryTranslator { + AbstractFurnaceInventoryTranslator(String javaBlockIdentifier, ContainerType containerType) { + super(3, javaBlockIdentifier, containerType, new ContainerInventoryUpdater()); + } + + @Override + public void updateProperty(GeyserSession session, Inventory inventory, int key, int value) { + ContainerSetDataPacket dataPacket = new ContainerSetDataPacket(); + dataPacket.setWindowId((byte) inventory.getId()); + switch (key) { + case 0: + dataPacket.setProperty(ContainerSetDataPacket.FURNACE_LIT_TIME); + break; + case 1: + dataPacket.setProperty(ContainerSetDataPacket.FURNACE_LIT_DURATION); + break; + case 2: + dataPacket.setProperty(ContainerSetDataPacket.FURNACE_TICK_COUNT); + break; + default: + return; + } + dataPacket.setValue(value); + session.sendUpstreamPacket(dataPacket); + } + + @Override + public SlotType getSlotType(int javaSlot) { + if (javaSlot == 2) + return SlotType.FURNACE_OUTPUT; + return SlotType.NORMAL; + } + + @Override + public BedrockContainerSlot javaSlotToBedrockContainer(int slot) { + if (slot == 1) { + return new BedrockContainerSlot(ContainerSlotType.FURNACE_FUEL, javaSlotToBedrock(slot)); + } + if (slot == 2) { + return new BedrockContainerSlot(ContainerSlotType.FURNACE_OUTPUT, javaSlotToBedrock(slot)); + } + return super.javaSlotToBedrockContainer(slot); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/furnace/BlastFurnaceInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/furnace/BlastFurnaceInventoryTranslator.java new file mode 100644 index 00000000..b7834dde --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/furnace/BlastFurnaceInventoryTranslator.java @@ -0,0 +1,44 @@ +/* + * 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.translators.furnace; + +import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; +import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; +import org.geysermc.connector.network.translators.inventory.BedrockContainerSlot; + +public class BlastFurnaceInventoryTranslator extends AbstractFurnaceInventoryTranslator { + public BlastFurnaceInventoryTranslator() { + super("minecraft:blast_furnace[facing=north,lit=false]", ContainerType.BLAST_FURNACE); + } + + @Override + public BedrockContainerSlot javaSlotToBedrockContainer(int slot) { + if (slot == 0) { + return new BedrockContainerSlot(ContainerSlotType.BLAST_FURNACE_INGREDIENT, javaSlotToBedrock(slot)); + } + return super.javaSlotToBedrockContainer(slot); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/furnace/FurnaceInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/furnace/FurnaceInventoryTranslator.java new file mode 100644 index 00000000..f3d75941 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/furnace/FurnaceInventoryTranslator.java @@ -0,0 +1,44 @@ +/* + * 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.translators.furnace; + +import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; +import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; +import org.geysermc.connector.network.translators.inventory.BedrockContainerSlot; + +public class FurnaceInventoryTranslator extends AbstractFurnaceInventoryTranslator { + public FurnaceInventoryTranslator() { + super("minecraft:furnace[facing=north,lit=false]", ContainerType.FURNACE); + } + + @Override + public BedrockContainerSlot javaSlotToBedrockContainer(int slot) { + if (slot == 0) { + return new BedrockContainerSlot(ContainerSlotType.FURNACE_INGREDIENT, javaSlotToBedrock(slot)); + } + return super.javaSlotToBedrockContainer(slot); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/furnace/SmokerInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/furnace/SmokerInventoryTranslator.java new file mode 100644 index 00000000..75eb33d9 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/furnace/SmokerInventoryTranslator.java @@ -0,0 +1,44 @@ +/* + * 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.translators.furnace; + +import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; +import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; +import org.geysermc.connector.network.translators.inventory.BedrockContainerSlot; + +public class SmokerInventoryTranslator extends AbstractFurnaceInventoryTranslator { + public SmokerInventoryTranslator() { + super("minecraft:smoker[facing=north,lit=false]", ContainerType.SMOKER); + } + + @Override + public BedrockContainerSlot javaSlotToBedrockContainer(int slot) { + if (slot == 0) { + return new BedrockContainerSlot(ContainerSlotType.SMOKER_INGREDIENT, javaSlotToBedrock(slot)); + } + return super.javaSlotToBedrockContainer(slot); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java index 44c1ad9b..11f20f97 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java @@ -42,7 +42,7 @@ import org.geysermc.connector.entity.player.PlayerEntity; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; -import org.geysermc.connector.network.translators.inventory.PlayerInventoryTranslator; +import org.geysermc.connector.network.translators.inventory.translators.PlayerInventoryTranslator; import org.geysermc.connector.utils.LocaleUtils; @Translator(packet = ServerNotifyClientPacket.class) 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 876717a8..cbdfa9c5 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java @@ -43,11 +43,9 @@ import org.geysermc.connector.common.ChatColor; 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.DoubleChestInventoryTranslator; +import org.geysermc.connector.network.translators.inventory.translators.chest.DoubleChestInventoryTranslator; import org.geysermc.connector.network.translators.inventory.InventoryTranslator; -import org.geysermc.connector.network.translators.item.ItemEntry; import org.geysermc.connector.network.translators.item.ItemRegistry; -import org.geysermc.connector.network.translators.item.ItemTranslator; import java.util.Collections; import java.util.Objects; From 33a86485dca06cff48e3e162c66927851e9db3ae Mon Sep 17 00:00:00 2001 From: DoctorMacc Date: Thu, 17 Dec 2020 11:46:11 -0500 Subject: [PATCH 05/39] Implement dropper/dispenser, hopper, shulker --- .../BedrockItemStackRequestTranslator.java | 1 + .../inventory/InventoryTranslator.java | 14 ++---- .../CraftingInventoryTranslator.java | 2 +- .../GenericBlockInventoryTranslator.java | 48 +++++++++++++++++++ .../ShulkerInventoryTranslator.java | 45 +++++++++++++++++ .../AbstractFurnaceInventoryTranslator.java | 2 +- .../updater/ContainerInventoryUpdater.java | 3 +- .../updater/CursorInventoryUpdater.java | 1 + 8 files changed, 104 insertions(+), 12 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/GenericBlockInventoryTranslator.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/ShulkerInventoryTranslator.java diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockItemStackRequestTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockItemStackRequestTranslator.java index 8190bb55..1ab68106 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockItemStackRequestTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockItemStackRequestTranslator.java @@ -38,6 +38,7 @@ public class BedrockItemStackRequestTranslator extends PacketTranslator Date: Thu, 17 Dec 2020 21:47:18 -0500 Subject: [PATCH 06/39] Brewing stand support; other attempts --- .../inventory/InventoryTranslator.java | 42 ++++++- .../BrewingInventoryTranslator.java | 113 ++++++++++++++++++ 2 files changed, 151 insertions(+), 4 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/BrewingInventoryTranslator.java 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 fe22d397..35bbb2cd 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,6 +26,7 @@ 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.data.game.window.WindowType; import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientCreativeInventoryActionPacket; import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; @@ -67,9 +68,9 @@ public abstract class InventoryTranslator { put(WindowType.GENERIC_9X6, new DoubleChestInventoryTranslator(54)); put(WindowType.CRAFTING, new CraftingInventoryTranslator()); put(WindowType.SHULKER_BOX, new ShulkerInventoryTranslator()); - /*put(WindowType.BREWING_STAND, new BrewingInventoryTranslator()); - put(WindowType.ANVIL, new AnvilInventoryTranslator()); - put(WindowType.GRINDSTONE, new GrindstoneInventoryTranslator());*/ + put(WindowType.BREWING_STAND, new BrewingInventoryTranslator()); + //put(WindowType.ANVIL, new AnvilInventoryTranslator()); + //put(WindowType.GRINDSTONE, new GrindstoneInventoryTranslator()); put(WindowType.MERCHANT, new MerchantInventoryTranslator()); //put(WindowType.SMITHING, new SmithingInventoryTranslator()); //put(WindowType.ENCHANTMENT, new EnchantmentInventoryTranslator()); //TODO @@ -81,6 +82,11 @@ public abstract class InventoryTranslator { put(WindowType.GENERIC_3X3, new GenericBlockInventoryTranslator(9, "minecraft:dispenser[facing=north,triggered=false]", ContainerType.DISPENSER)); put(WindowType.HOPPER, new GenericBlockInventoryTranslator(5, "minecraft:hopper[enabled=false,facing=down]", ContainerType.HOPPER)); //put(WindowType.BEACON, new AbstractBlockInventoryTranslator(1, "minecraft:beacon", ContainerType.BEACON)); //TODO*/ + + //put(WindowType.CARTOGRAPHY + //put(WindowType.STONECUTTER + //put(WindowType.LOOM + //put(WindowType. } }; @@ -129,8 +135,12 @@ public abstract class InventoryTranslator { case TAKE: case PLACE: { TransferStackRequestActionData transferAction = (TransferStackRequestActionData) action; - if (!(checkNetId(session, inventory, transferAction.getSource()) && checkNetId(session, inventory, transferAction.getDestination()))) + if (!(checkNetId(session, inventory, transferAction.getSource()) && checkNetId(session, inventory, transferAction.getDestination()))) { + session.getConnector().getLogger().error("DEBUG: About to reject request."); + session.getConnector().getLogger().error("Source: " + transferAction.getSource().toString() + " Result: " + checkNetId(session, inventory, transferAction.getSource())); + session.getConnector().getLogger().error("Destination: " + transferAction.getDestination().toString() + " Result: " + checkNetId(session, inventory, transferAction.getDestination())); return rejectRequest(request); + } if (isCursor(transferAction.getSource()) && isCursor(transferAction.getDestination())) { //??? return rejectRequest(request); @@ -273,6 +283,30 @@ public abstract class InventoryTranslator { CraftCreativeStackRequestActionData creativeAction = (CraftCreativeStackRequestActionData) action; System.out.println(creativeAction.getCreativeItemNetworkId()); } + case DESTROY: { + //TODO: Yeah this doesn't work yet. + + // Only called when a creative client wants to destroy an item... I think - Camotoy + DestroyStackRequestActionData destroyAction = (DestroyStackRequestActionData) action; + if (session.getGameMode() == GameMode.CREATIVE) { + if (isCursor(destroyAction.getSource())) { + session.getPlayerInventory().setCursor(GeyserItemStack.EMPTY); + return acceptRequest(request, makeContainerEntries(session, inventory, Collections.emptySet())); + } else { + int javaSlot = bedrockSlotToJava(destroyAction.getSource()); + inventory.setItem(javaSlot, GeyserItemStack.EMPTY); + ClientCreativeInventoryActionPacket creativeActionPacket = new ClientCreativeInventoryActionPacket( + javaSlot, + new ItemStack(0) + ); + session.sendDownstreamPacket(creativeActionPacket); + Set affectedSlots = Collections.singleton(javaSlot); + return acceptRequest(request, makeContainerEntries(session, inventory, affectedSlots)); + } + } else { + return rejectRequest(request); + } + } default: return rejectRequest(request); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/BrewingInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/BrewingInventoryTranslator.java new file mode 100644 index 00000000..6bc30a90 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/BrewingInventoryTranslator.java @@ -0,0 +1,113 @@ +/* + * 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.translators; + +import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; +import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; +import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData; +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.BedrockContainerSlot; +import org.geysermc.connector.network.translators.inventory.updater.ContainerInventoryUpdater; + +public class BrewingInventoryTranslator extends AbstractBlockInventoryTranslator { + public BrewingInventoryTranslator() { + super(5, "minecraft:brewing_stand[has_bottle_0=false,has_bottle_1=false,has_bottle_2=false]", ContainerType.BREWING_STAND, ContainerInventoryUpdater.INSTANCE); + } + + @Override + public void openInventory(GeyserSession session, Inventory inventory) { + super.openInventory(session, inventory); + ContainerSetDataPacket dataPacket = new ContainerSetDataPacket(); + dataPacket.setWindowId((byte) inventory.getId()); + dataPacket.setProperty(ContainerSetDataPacket.BREWING_STAND_FUEL_TOTAL); + dataPacket.setValue(20); + session.sendUpstreamPacket(dataPacket); + } + + @Override + public void updateProperty(GeyserSession session, Inventory inventory, int key, int value) { + ContainerSetDataPacket dataPacket = new ContainerSetDataPacket(); + dataPacket.setWindowId((byte) inventory.getId()); + switch (key) { + case 0: + dataPacket.setProperty(ContainerSetDataPacket.BREWING_STAND_BREW_TIME); + break; + case 1: + dataPacket.setProperty(ContainerSetDataPacket.BREWING_STAND_FUEL_AMOUNT); + break; + default: + return; + } + dataPacket.setValue(value); + session.sendUpstreamPacket(dataPacket); + } + + @Override + public int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData) { + System.out.println("Brewing stand: " + slotInfoData); + if (slotInfoData.getContainer() == ContainerSlotType.BREWING_INPUT) { + // Ingredient + // TODO: This hasn't worked and then suddenly, it did. + return 3; + } + if (slotInfoData.getContainer() == ContainerSlotType.BREWING_RESULT) { + // Potions + return slotInfoData.getSlot() - 1; + } + return super.bedrockSlotToJava(slotInfoData); + } + + @Override + public int javaSlotToBedrock(int slot) { + switch (slot) { + case 0: + return 1; + case 1: + return 2; + case 2: + return 3; + case 3: + return 0; + } + return super.javaSlotToBedrock(slot); + } + + @Override + public BedrockContainerSlot javaSlotToBedrockContainer(int slot) { + if (slot == 0 || slot == 1 || slot == 2) { + return new BedrockContainerSlot(ContainerSlotType.BREWING_RESULT, javaSlotToBedrock(slot)); + } + if (slot == 3) { + return new BedrockContainerSlot(ContainerSlotType.BREWING_INPUT, 0); + } + if (slot == 4) { + return new BedrockContainerSlot(ContainerSlotType.BREWING_FUEL, 4); + } + return super.javaSlotToBedrockContainer(slot); + } +} From f47cf32d903c2822da814f17c33e0e6c5f2de851 Mon Sep 17 00:00:00 2001 From: DoctorMacc Date: Sat, 19 Dec 2020 19:19:44 -0500 Subject: [PATCH 07/39] Make my life tolerable --- .../translators/inventory/InventoryTranslator.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) 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 35bbb2cd..52fb6e96 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 @@ -136,6 +136,10 @@ public abstract class InventoryTranslator { case PLACE: { TransferStackRequestActionData transferAction = (TransferStackRequestActionData) action; if (!(checkNetId(session, inventory, transferAction.getSource()) && checkNetId(session, inventory, transferAction.getDestination()))) { + if (session.getGameMode().equals(GameMode.CREATIVE) && transferAction.getSource().getContainer() == ContainerSlotType.CRAFTING_INPUT && + transferAction.getSource().getSlot() >= 28 && transferAction.getSource().getSlot() <= 31) { + return rejectRequest(request, false); + } session.getConnector().getLogger().error("DEBUG: About to reject request."); session.getConnector().getLogger().error("Source: " + transferAction.getSource().toString() + " Result: " + checkNetId(session, inventory, transferAction.getSource())); session.getConnector().getLogger().error("Destination: " + transferAction.getDestination().toString() + " Result: " + checkNetId(session, inventory, transferAction.getDestination())); @@ -504,7 +508,14 @@ public abstract class InventoryTranslator { } public static ItemStackResponsePacket.Response rejectRequest(ItemStackRequestPacket.Request request) { - new Throwable("DEBUGGING: ItemStackRequest rejected").printStackTrace(); //TODO: temporary debugging + return rejectRequest(request, true); + } + + public static ItemStackResponsePacket.Response rejectRequest(ItemStackRequestPacket.Request request, boolean throwError) { + if (throwError) { + // Currently for debugging, but might be worth it to keep in the future if something goes terribly wrong. + new Throwable("DEBUGGING: ItemStackRequest rejected").printStackTrace(); + } return new ItemStackResponsePacket.Response(ItemStackResponsePacket.ResponseStatus.ERROR, request.getRequestId(), Collections.emptyList()); } From c6b4d163a1634d1c94b8c027555208d546160502 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Mon, 21 Dec 2020 21:09:14 -0500 Subject: [PATCH 08/39] Improve creative support --- .../inventory/InventoryTranslator.java | 130 ++++++++++++++---- .../inventory/click/ClickPlan.java | 12 +- .../java/window/JavaSetSlotTranslator.java | 2 +- 3 files changed, 117 insertions(+), 27 deletions(-) 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 52fb6e96..f532beea 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 @@ -31,10 +31,13 @@ import com.github.steveice10.mc.protocol.data.game.window.WindowType; import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientCreativeInventoryActionPacket; import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; +import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData; import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.*; import com.nukkitx.protocol.bedrock.packet.ItemStackRequestPacket; import com.nukkitx.protocol.bedrock.packet.ItemStackResponsePacket; +import it.unimi.dsi.fastutil.ints.IntOpenHashSet; +import it.unimi.dsi.fastutil.ints.IntSet; import lombok.AllArgsConstructor; import org.geysermc.connector.inventory.GeyserItemStack; import org.geysermc.connector.inventory.Inventory; @@ -92,6 +95,7 @@ public abstract class InventoryTranslator { public static final int PLAYER_INVENTORY_SIZE = 36; public static final int PLAYER_INVENTORY_OFFSET = 9; + private static final int MAX_ITEM_STACK_SIZE = 64; public final int size; public abstract void prepareInventory(GeyserSession session, Inventory inventory); @@ -114,6 +118,7 @@ public abstract class InventoryTranslator { if (firstAction.getType() == StackRequestActionType.CRAFT_RECIPE || firstAction.getType() == StackRequestActionType.CRAFT_RECIPE_AUTO) { responsePacket.getEntries().add(translateCraftingRequest(session, inventory, request)); } else if (firstAction.getType() == StackRequestActionType.CRAFT_CREATIVE) { + // This is also used for pulling items out of creative responsePacket.getEntries().add(translateCreativeRequest(session, inventory, request)); } else { responsePacket.getEntries().add(translateRequest(session, inventory, request)); @@ -129,6 +134,7 @@ public abstract class InventoryTranslator { public ItemStackResponsePacket.Response translateRequest(GeyserSession session, Inventory inventory, ItemStackRequestPacket.Request request) { System.out.println(request); ClickPlan plan = new ClickPlan(session, this, inventory); + IntSet affectedSlots = new IntOpenHashSet(); for (StackRequestActionData action : request.getActions()) { GeyserItemStack cursor = session.getPlayerInventory().getCursor(); switch (action.getType()) { @@ -148,6 +154,73 @@ public abstract class InventoryTranslator { if (isCursor(transferAction.getSource()) && isCursor(transferAction.getDestination())) { //??? return rejectRequest(request); + } else if (session.getGameMode().equals(GameMode.CREATIVE) && inventory instanceof PlayerInventory) { // TODO: does the Java server use the player inventory in all instances? + // Creative acts a little differently because it just edits slots + int sourceSlot = bedrockSlotToJava(transferAction.getSource()); + int destSlot = bedrockSlotToJava(transferAction.getDestination()); + boolean sourceIsCursor = isCursor(transferAction.getSource()); + boolean destIsCursor = isCursor(transferAction.getDestination()); + + GeyserItemStack sourceItem = sourceIsCursor ? session.getPlayerInventory().getCursor() : + inventory.getItem(sourceSlot); + GeyserItemStack newItem = sourceItem.copy(); + if (sourceIsCursor) { + GeyserItemStack destItem = inventory.getItem(destSlot); + if (destItem.getId() == sourceItem.getId()) { + // Combining items + int itemsLeftOver = destItem.getAmount() + transferAction.getCount(); + if (itemsLeftOver > MAX_ITEM_STACK_SIZE) { + // Items will remain in cursor because destination slot gets set to 64 + destItem.setAmount(MAX_ITEM_STACK_SIZE); + sourceItem.setAmount(itemsLeftOver - MAX_ITEM_STACK_SIZE); + } else { + // Cursor will be emptied + destItem.setAmount(itemsLeftOver); + session.getPlayerInventory().setCursor(GeyserItemStack.EMPTY); + } + ClientCreativeInventoryActionPacket creativeActionPacket = new ClientCreativeInventoryActionPacket( + destSlot, + destItem.getItemStack() + ); + session.sendDownstreamPacket(creativeActionPacket); + affectedSlots.add(destSlot); + break; + } + } + // Update the item count with however much the client took + newItem.setAmount(transferAction.getCount()); + // Remove that amount from the existing item + sourceItem.setAmount(sourceItem.getAmount() - transferAction.getCount()); + if (sourceItem.isEmpty()) { + // Item is basically deleted + if (sourceIsCursor) { + session.getPlayerInventory().setCursor(GeyserItemStack.EMPTY); + } else { + inventory.setItem(sourceSlot, GeyserItemStack.EMPTY); + } + } + if (destIsCursor) { + session.getPlayerInventory().setCursor(newItem); + } else { + inventory.setItem(destSlot, newItem); + } + GeyserItemStack itemToUpdate = destIsCursor ? sourceItem : newItem; + // The Java server doesn't care about what's in the mouse in creative mode, so we just need to track + // which inventory slot the client modified + ClientCreativeInventoryActionPacket creativeActionPacket = new ClientCreativeInventoryActionPacket( + destIsCursor ? sourceSlot : destSlot, + itemToUpdate.isEmpty() ? new ItemStack(0) : itemToUpdate.getItemStack() + ); + session.sendDownstreamPacket(creativeActionPacket); + System.out.println(creativeActionPacket); + + if (!sourceIsCursor) { // Cursor is always added for us as an affected slot + affectedSlots.add(sourceSlot); + } + if (!destIsCursor) { + affectedSlots.add(destSlot); + } + } else if (isCursor(transferAction.getSource())) { //releasing cursor int sourceAmount = cursor.getAmount(); int destSlot = bedrockSlotToJava(transferAction.getDestination()); @@ -286,37 +359,38 @@ public abstract class InventoryTranslator { case CRAFT_CREATIVE: { CraftCreativeStackRequestActionData creativeAction = (CraftCreativeStackRequestActionData) action; System.out.println(creativeAction.getCreativeItemNetworkId()); + break; } case DESTROY: { - //TODO: Yeah this doesn't work yet. - // Only called when a creative client wants to destroy an item... I think - Camotoy DestroyStackRequestActionData destroyAction = (DestroyStackRequestActionData) action; - if (session.getGameMode() == GameMode.CREATIVE) { - if (isCursor(destroyAction.getSource())) { - session.getPlayerInventory().setCursor(GeyserItemStack.EMPTY); - return acceptRequest(request, makeContainerEntries(session, inventory, Collections.emptySet())); - } else { - int javaSlot = bedrockSlotToJava(destroyAction.getSource()); - inventory.setItem(javaSlot, GeyserItemStack.EMPTY); - ClientCreativeInventoryActionPacket creativeActionPacket = new ClientCreativeInventoryActionPacket( - javaSlot, - new ItemStack(0) - ); - session.sendDownstreamPacket(creativeActionPacket); - Set affectedSlots = Collections.singleton(javaSlot); - return acceptRequest(request, makeContainerEntries(session, inventory, affectedSlots)); - } - } else { + if (!session.getGameMode().equals(GameMode.CREATIVE)) { + // If this happens, let's throw an error and figure out why. return rejectRequest(request); } + if (!isCursor(destroyAction.getSource())) { + int javaSlot = bedrockSlotToJava(destroyAction.getSource()); + ClientCreativeInventoryActionPacket destroyItemPacket = new ClientCreativeInventoryActionPacket( + javaSlot, + new ItemStack(0) + ); + session.sendDownstreamPacket(destroyItemPacket); + System.out.println(destroyItemPacket); + inventory.setItem(javaSlot, GeyserItemStack.EMPTY); + affectedSlots.add(javaSlot); + } else { + // Just sync up the item on our end, since the server doesn't care what's in our cursor + session.getPlayerInventory().setCursor(GeyserItemStack.EMPTY); + } + break; } default: return rejectRequest(request); } } plan.execute(false); - return acceptRequest(request, makeContainerEntries(session, inventory, plan.getAffectedSlots())); + affectedSlots.addAll(plan.getAffectedSlots()); + return acceptRequest(request, makeContainerEntries(session, inventory, affectedSlots)); } public ItemStackResponsePacket.Response translateCraftingRequest(GeyserSession session, Inventory inventory, ItemStackRequestPacket.Request request) { @@ -480,18 +554,26 @@ public abstract class InventoryTranslator { if (transferAction.getSource().getContainer() != ContainerSlotType.CREATIVE_OUTPUT) { return rejectRequest(request); } + // Reference the creative items list we send to the client to know what it's asking of us + ItemData creativeItem = ItemRegistry.CREATIVE_ITEMS[creativeId - 1]; + // Get the correct count + creativeItem = ItemData.of(creativeItem.getId(), creativeItem.getDamage(), transferAction.getCount(), creativeItem.getTag()); + ItemStack javaCreativeItem = ItemTranslator.translateToJava(creativeItem); + if (isCursor(transferAction.getDestination())) { - session.getPlayerInventory().setCursor(GeyserItemStack.from(ItemTranslator.translateToJava(ItemRegistry.CREATIVE_ITEMS[creativeId]), session.getItemNetId().getAndIncrement())); //TODO - return acceptRequest(request, makeContainerEntries(session, inventory, Collections.emptySet())); + session.getPlayerInventory().setCursor(GeyserItemStack.from(javaCreativeItem, session.getItemNetId().getAndIncrement())); + return acceptRequest(request, Collections.singletonList( + new ItemStackResponsePacket.ContainerEntry(ContainerSlotType.CURSOR, + Collections.singletonList(makeItemEntry(session, 0, session.getPlayerInventory().getCursor()))))); } else { int javaSlot = bedrockSlotToJava(transferAction.getDestination()); - ItemStack javaItem = ItemTranslator.translateToJava(ItemRegistry.CREATIVE_ITEMS[creativeId - 1]); //TODO - inventory.setItem(javaSlot, GeyserItemStack.from(javaItem, session.getItemNetId().getAndIncrement())); + inventory.setItem(javaSlot, GeyserItemStack.from(javaCreativeItem, session.getItemNetId().getAndIncrement())); ClientCreativeInventoryActionPacket creativeActionPacket = new ClientCreativeInventoryActionPacket( javaSlot, - javaItem + javaCreativeItem ); session.sendDownstreamPacket(creativeActionPacket); + System.out.println(creativeActionPacket); Set affectedSlots = Collections.singleton(javaSlot); return acceptRequest(request, makeContainerEntries(session, inventory, affectedSlots)); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/ClickPlan.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/ClickPlan.java index f4cf3561..4e5c2855 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/ClickPlan.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/ClickPlan.java @@ -31,6 +31,8 @@ import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientConfi import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientWindowActionPacket; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.ints.IntOpenHashSet; +import it.unimi.dsi.fastutil.ints.IntSet; import lombok.Value; import org.geysermc.connector.inventory.GeyserItemStack; import org.geysermc.connector.inventory.Inventory; @@ -193,8 +195,11 @@ public class ClickPlan { } } - public Set getAffectedSlots() { - Set affectedSlots = new HashSet<>(); + /** + * @return a new set of all affected slots. This isn't a constant variable; it's newly generated each time it is run. + */ + public IntSet getAffectedSlots() { + IntSet affectedSlots = new IntOpenHashSet(); for (ClickAction action : plan) { if (translator.getSlotType(action.slot) == SlotType.NORMAL && action.slot != Click.OUTSIDE_SLOT) { affectedSlots.add(action.slot); @@ -206,6 +211,9 @@ public class ClickPlan { @Value private static class ClickAction { Click click; + /** + * Java slot + */ int slot; } } 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 22a0edca..531242fc 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 @@ -26,7 +26,6 @@ package org.geysermc.connector.network.translators.java.window; import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerSetSlotPacket; -import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import org.geysermc.connector.inventory.GeyserItemStack; import org.geysermc.connector.inventory.Inventory; import org.geysermc.connector.network.session.GeyserSession; @@ -40,6 +39,7 @@ public class JavaSetSlotTranslator extends PacketTranslator @Override public void translate(ServerSetSlotPacket packet, GeyserSession session) { + System.out.println(packet.toString()); session.addInventoryTask(() -> { if (packet.getWindowId() == 255) { //cursor GeyserItemStack newItem = GeyserItemStack.from(packet.getItem()); From 009905184eb468212403a73375ec78157472ad7d Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Mon, 21 Dec 2020 22:44:01 -0500 Subject: [PATCH 09/39] Add grindstone and smithing table --- .../inventory/InventoryTranslator.java | 17 +++- .../EnchantingInventoryTranslator.java | 35 +++++++++ .../GrindstoneInventoryTranslator.java | 78 +++++++++++++++++++ .../SmithingInventoryTranslator.java | 78 +++++++++++++++++++ 4 files changed, 205 insertions(+), 3 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/EnchantingInventoryTranslator.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/GrindstoneInventoryTranslator.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/SmithingInventoryTranslator.java 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 f532beea..cf63811a 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 @@ -73,9 +73,9 @@ public abstract class InventoryTranslator { put(WindowType.SHULKER_BOX, new ShulkerInventoryTranslator()); put(WindowType.BREWING_STAND, new BrewingInventoryTranslator()); //put(WindowType.ANVIL, new AnvilInventoryTranslator()); - //put(WindowType.GRINDSTONE, new GrindstoneInventoryTranslator()); + put(WindowType.GRINDSTONE, new GrindstoneInventoryTranslator()); put(WindowType.MERCHANT, new MerchantInventoryTranslator()); - //put(WindowType.SMITHING, new SmithingInventoryTranslator()); + put(WindowType.SMITHING, new SmithingInventoryTranslator()); //put(WindowType.ENCHANTMENT, new EnchantmentInventoryTranslator()); //TODO put(WindowType.FURNACE, new FurnaceInventoryTranslator()); @@ -154,7 +154,7 @@ public abstract class InventoryTranslator { if (isCursor(transferAction.getSource()) && isCursor(transferAction.getDestination())) { //??? return rejectRequest(request); - } else if (session.getGameMode().equals(GameMode.CREATIVE) && inventory instanceof PlayerInventory) { // TODO: does the Java server use the player inventory in all instances? + } else if (session.getGameMode().equals(GameMode.CREATIVE) && inventory instanceof PlayerInventory) { // TODO: does the Java server use this stuff all the time in creative? // Creative acts a little differently because it just edits slots int sourceSlot = bedrockSlotToJava(transferAction.getSource()); int destSlot = bedrockSlotToJava(transferAction.getDestination()); @@ -369,6 +369,7 @@ public abstract class InventoryTranslator { return rejectRequest(request); } if (!isCursor(destroyAction.getSource())) { + // Item exists; let's remove it from the inventory int javaSlot = bedrockSlotToJava(destroyAction.getSource()); ClientCreativeInventoryActionPacket destroyItemPacket = new ClientCreativeInventoryActionPacket( javaSlot, @@ -384,6 +385,16 @@ public abstract class InventoryTranslator { } break; } + // These three are called for the grindstone + case CONSUME: { + break; + } + case CRAFT_NON_IMPLEMENTED_DEPRECATED: { + break; + } + case CRAFT_RESULTS_DEPRECATED: { + break; + } default: return rejectRequest(request); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/EnchantingInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/EnchantingInventoryTranslator.java new file mode 100644 index 00000000..a8b13be4 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/EnchantingInventoryTranslator.java @@ -0,0 +1,35 @@ +/* + * 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.translators; + +import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; +import org.geysermc.connector.network.translators.inventory.updater.CursorInventoryUpdater; + +public class EnchantingInventoryTranslator extends AbstractBlockInventoryTranslator { + public EnchantingInventoryTranslator() { + super(2, "minecraft:enchanting_table", ContainerType.ENCHANTMENT, CursorInventoryUpdater.INSTANCE); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/GrindstoneInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/GrindstoneInventoryTranslator.java new file mode 100644 index 00000000..684b25e8 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/GrindstoneInventoryTranslator.java @@ -0,0 +1,78 @@ +/* + * 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.translators; + +import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; +import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; +import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData; +import org.geysermc.connector.network.translators.inventory.BedrockContainerSlot; +import org.geysermc.connector.network.translators.inventory.updater.CursorInventoryUpdater; + +public class GrindstoneInventoryTranslator extends AbstractBlockInventoryTranslator { + public GrindstoneInventoryTranslator() { + super(3, "minecraft:grindstone[face=floor,facing=north]", ContainerType.GRINDSTONE, CursorInventoryUpdater.INSTANCE); + } + + @Override + public int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData) { + if (slotInfoData.getContainer() == ContainerSlotType.GRINDSTONE_INPUT) { + return 0; + } + if (slotInfoData.getContainer() == ContainerSlotType.GRINDSTONE_ADDITIONAL) { + return 1; + } + if (slotInfoData.getContainer() == ContainerSlotType.GRINDSTONE_RESULT || slotInfoData.getContainer() == ContainerSlotType.CREATIVE_OUTPUT) { + return 2; + } + return super.bedrockSlotToJava(slotInfoData); + } + + @Override + public BedrockContainerSlot javaSlotToBedrockContainer(int slot) { + switch (slot) { + case 0: + return new BedrockContainerSlot(ContainerSlotType.GRINDSTONE_INPUT, 16); + case 1: + return new BedrockContainerSlot(ContainerSlotType.GRINDSTONE_ADDITIONAL, 17); + case 2: + return new BedrockContainerSlot(ContainerSlotType.GRINDSTONE_RESULT, 50); + } + return super.javaSlotToBedrockContainer(slot); + } + + @Override + public int javaSlotToBedrock(int slot) { + switch (slot) { + case 0: + return 16; + case 1: + return 17; + case 2: + return 50; + } + return super.javaSlotToBedrock(slot); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/SmithingInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/SmithingInventoryTranslator.java new file mode 100644 index 00000000..fba8a8d6 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/SmithingInventoryTranslator.java @@ -0,0 +1,78 @@ +/* + * 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.translators; + +import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; +import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; +import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData; +import org.geysermc.connector.network.translators.inventory.BedrockContainerSlot; +import org.geysermc.connector.network.translators.inventory.updater.CursorInventoryUpdater; + +public class SmithingInventoryTranslator extends AbstractBlockInventoryTranslator { + public SmithingInventoryTranslator() { + super(3, "minecraft:smithing_table", ContainerType.SMITHING_TABLE, CursorInventoryUpdater.INSTANCE); + } + + @Override + public int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData) { + if (slotInfoData.getContainer() == ContainerSlotType.SMITHING_TABLE_INPUT) { + return 0; + } + if (slotInfoData.getContainer() == ContainerSlotType.SMITHING_TABLE_MATERIAL) { + return 1; + } + if (slotInfoData.getContainer() == ContainerSlotType.SMITHING_TABLE_RESULT || slotInfoData.getContainer() == ContainerSlotType.CREATIVE_OUTPUT) { + return 2; + } + return super.bedrockSlotToJava(slotInfoData); + } + + @Override + public BedrockContainerSlot javaSlotToBedrockContainer(int slot) { + switch (slot) { + case 0: + return new BedrockContainerSlot(ContainerSlotType.SMITHING_TABLE_INPUT, 51); + case 1: + return new BedrockContainerSlot(ContainerSlotType.SMITHING_TABLE_MATERIAL, 52); + case 2: + return new BedrockContainerSlot(ContainerSlotType.SMITHING_TABLE_RESULT, 50); + } + return super.javaSlotToBedrockContainer(slot); + } + + @Override + public int javaSlotToBedrock(int slot) { + switch (slot) { + case 0: + return 51; + case 1: + return 52; + case 2: + return 50; + } + return super.javaSlotToBedrock(slot); + } +} From f4f804e1ca8045f7a25c92fc470d18bb996c4de5 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 23 Dec 2020 01:21:00 -0500 Subject: [PATCH 10/39] Enchantment table works; anvil is almost there --- .../connector/inventory/AnvilContainer.java | 34 ++++ .../inventory/EnchantingContainer.java | 53 +++++ .../inventory/EnchantmentInventory.java | 40 ---- .../connector/inventory/FurnaceInventory.java | 1 + .../inventory/GeyserEnchantOption.java | 78 ++++++++ .../bedrock/BedrockFilterTextTranslator.java | 47 +++++ .../inventory/InventoryTranslator.java | 88 ++++++-- .../translators/AnvilInventoryTranslator.java | 87 ++++++++ .../CraftingInventoryTranslator.java | 4 +- .../EnchantingInventoryTranslator.java | 188 +++++++++++++++++- .../GrindstoneInventoryTranslator.java | 4 +- .../MerchantInventoryTranslator.java | 3 +- .../SmithingInventoryTranslator.java | 4 +- .../updater/CursorInventoryUpdater.java | 65 ------ .../inventory/updater/UIInventoryUpdater.java | 2 +- .../window/JavaWindowPropertyTranslator.java | 1 + 16 files changed, 566 insertions(+), 133 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/inventory/AnvilContainer.java create mode 100644 connector/src/main/java/org/geysermc/connector/inventory/EnchantingContainer.java delete mode 100644 connector/src/main/java/org/geysermc/connector/inventory/EnchantmentInventory.java create mode 100644 connector/src/main/java/org/geysermc/connector/inventory/GeyserEnchantOption.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockFilterTextTranslator.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/AnvilInventoryTranslator.java delete mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/CursorInventoryUpdater.java diff --git a/connector/src/main/java/org/geysermc/connector/inventory/AnvilContainer.java b/connector/src/main/java/org/geysermc/connector/inventory/AnvilContainer.java new file mode 100644 index 00000000..d940ac75 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/inventory/AnvilContainer.java @@ -0,0 +1,34 @@ +/* + * 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.inventory; + +import com.github.steveice10.mc.protocol.data.game.window.WindowType; + +public class AnvilContainer extends Container { + public AnvilContainer(String title, int id, WindowType windowType, int size, PlayerInventory playerInventory) { + super(title, id, windowType, size, playerInventory); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/inventory/EnchantingContainer.java b/connector/src/main/java/org/geysermc/connector/inventory/EnchantingContainer.java new file mode 100644 index 00000000..c8b2bef1 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/inventory/EnchantingContainer.java @@ -0,0 +1,53 @@ +/* + * 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.inventory; + +import com.github.steveice10.mc.protocol.data.game.window.WindowType; +import com.nukkitx.protocol.bedrock.data.inventory.EnchantOptionData; +import lombok.Getter; + +public class EnchantingContainer extends Container { + /** + * A cache of what Bedrock sees + */ + @Getter + private final EnchantOptionData[] enchantOptions; + /** + * A mutable cache of what the server sends us + */ + @Getter + private final GeyserEnchantOption[] geyserEnchantOptions; + + public EnchantingContainer(String title, int id, WindowType windowType, int size, PlayerInventory playerInventory) { + super(title, id, windowType, size, playerInventory); + + enchantOptions = new EnchantOptionData[3]; + geyserEnchantOptions = new GeyserEnchantOption[3]; + for (int i = 0; i < geyserEnchantOptions.length; i++) { + geyserEnchantOptions[i] = new GeyserEnchantOption(i); + } + } +} diff --git a/connector/src/main/java/org/geysermc/connector/inventory/EnchantmentInventory.java b/connector/src/main/java/org/geysermc/connector/inventory/EnchantmentInventory.java deleted file mode 100644 index 65debc48..00000000 --- a/connector/src/main/java/org/geysermc/connector/inventory/EnchantmentInventory.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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.inventory; - -import com.github.steveice10.mc.protocol.data.game.window.WindowType; -import com.nukkitx.protocol.bedrock.data.inventory.EnchantOptionData; -import lombok.Getter; - -@Getter -public class EnchantmentInventory extends Inventory { - private EnchantOptionData[] enchantOptions; - - public EnchantmentInventory(String title, int id, WindowType windowType, int size) { - super(title, id, windowType, size); - } -} diff --git a/connector/src/main/java/org/geysermc/connector/inventory/FurnaceInventory.java b/connector/src/main/java/org/geysermc/connector/inventory/FurnaceInventory.java index 4dc098d8..0af76244 100644 --- a/connector/src/main/java/org/geysermc/connector/inventory/FurnaceInventory.java +++ b/connector/src/main/java/org/geysermc/connector/inventory/FurnaceInventory.java @@ -30,6 +30,7 @@ import com.github.steveice10.mc.protocol.data.game.window.WindowType; import lombok.Getter; import lombok.Setter; +//TODO: Figure out what this is and if we should remove it @Getter public class FurnaceInventory extends Inventory { @Setter diff --git a/connector/src/main/java/org/geysermc/connector/inventory/GeyserEnchantOption.java b/connector/src/main/java/org/geysermc/connector/inventory/GeyserEnchantOption.java new file mode 100644 index 00000000..ea58372e --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/inventory/GeyserEnchantOption.java @@ -0,0 +1,78 @@ +/* + * 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.inventory; + +import com.nukkitx.protocol.bedrock.data.inventory.EnchantData; +import com.nukkitx.protocol.bedrock.data.inventory.EnchantOptionData; +import lombok.Getter; +import lombok.Setter; +import org.geysermc.connector.network.session.GeyserSession; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * A mutable "wrapper" around {@link EnchantOptionData} + */ +@Setter +public class GeyserEnchantOption { + private static final List EMPTY = Collections.emptyList(); + /** + * This: https://cdn.discordapp.com/attachments/613168850925649981/791030657169227816/unknown.png + * is controlled by the server. + * So, of course, we have to throw in some easter eggs. ;) + */ + private static final List ENCHANT_NAMES = Arrays.asList("tougher armor", "lukeeey", "fall better", + "explode less", "camo toy", "breathe better", "rtm five one six", "armor stab", "water walk", "you are elsa", + "tim two zero three", "fast walk nether", "oof ouch owie", "enemy on fire", "spider sad", "aj ferguson", "redned", + "more items thx", "long sword reach", "fast tool", "give me block", "less breaky break", "cube craft", + "strong arrow", "fist arrow", "spicy arrow", "many many arrows", "geyser", "come here fish", "i like this", + "stabby stab", "supreme mortal", "avatar i guess", "more arrows", "fly finder seventeen", "in and out", + "xp heals tools", "dragon proxy waz here"); + + @Getter + private final int javaIndex; + + private int xpCost = 0; + private int javaEnchantIndex = -1; + private int bedrockEnchantIndex = -1; + private int enchantLevel = -1; + + public GeyserEnchantOption(int javaIndex) { + this.javaIndex = javaIndex; + } + + public EnchantOptionData build(GeyserSession session) { + if (enchantLevel == -1) { + // Should not be sent to the client, as it is supposed to be empty + return null; + } + return new EnchantOptionData(xpCost, javaIndex + 16, EMPTY, + Collections.singletonList(new EnchantData(bedrockEnchantIndex, enchantLevel)), EMPTY, + javaEnchantIndex == -1 ? "unknown" : ENCHANT_NAMES.get(javaEnchantIndex), session.getItemNetId().incrementAndGet()); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockFilterTextTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockFilterTextTranslator.java new file mode 100644 index 00000000..cf06acf5 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockFilterTextTranslator.java @@ -0,0 +1,47 @@ +/* + * 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.bedrock; + +import com.nukkitx.protocol.bedrock.packet.FilterTextPacket; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.PacketTranslator; +import org.geysermc.connector.network.translators.Translator; + +/** + * Used to send strings to the client and filter out unwanted words. + * Java doesn't care, so we don't care, and we approve all strings. + */ +@Translator(packet = FilterTextPacket.class) +public class BedrockFilterTextTranslator extends PacketTranslator { + + @Override + public void translate(FilterTextPacket packet, GeyserSession session) { + // TODO: Bedrock doesn't send this. Why? + System.out.println(packet.toString()); + packet.setFromServer(true); + session.sendUpstreamPacket(packet); + } +} 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 cf63811a..719a2092 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 @@ -62,29 +62,36 @@ public abstract class InventoryTranslator { public static final Map INVENTORY_TRANSLATORS = new HashMap() { { + /* Player Inventory */ put(null, new PlayerInventoryTranslator()); //player inventory + + /* Chest UIs */ put(WindowType.GENERIC_9X1, new SingleChestInventoryTranslator(9)); put(WindowType.GENERIC_9X2, new SingleChestInventoryTranslator(18)); put(WindowType.GENERIC_9X3, new SingleChestInventoryTranslator(27)); put(WindowType.GENERIC_9X4, new DoubleChestInventoryTranslator(36)); put(WindowType.GENERIC_9X5, new DoubleChestInventoryTranslator(45)); put(WindowType.GENERIC_9X6, new DoubleChestInventoryTranslator(54)); - put(WindowType.CRAFTING, new CraftingInventoryTranslator()); - put(WindowType.SHULKER_BOX, new ShulkerInventoryTranslator()); - put(WindowType.BREWING_STAND, new BrewingInventoryTranslator()); - //put(WindowType.ANVIL, new AnvilInventoryTranslator()); - put(WindowType.GRINDSTONE, new GrindstoneInventoryTranslator()); - put(WindowType.MERCHANT, new MerchantInventoryTranslator()); - put(WindowType.SMITHING, new SmithingInventoryTranslator()); - //put(WindowType.ENCHANTMENT, new EnchantmentInventoryTranslator()); //TODO + /* Furnaces */ put(WindowType.FURNACE, new FurnaceInventoryTranslator()); put(WindowType.BLAST_FURNACE, new BlastFurnaceInventoryTranslator()); put(WindowType.SMOKER, new SmokerInventoryTranslator()); + /* Specific Inventories */ + put(WindowType.ANVIL, new AnvilInventoryTranslator()); + put(WindowType.BREWING_STAND, new BrewingInventoryTranslator()); + put(WindowType.CRAFTING, new CraftingInventoryTranslator()); + put(WindowType.ENCHANTMENT, new EnchantingInventoryTranslator()); + put(WindowType.GRINDSTONE, new GrindstoneInventoryTranslator()); + put(WindowType.MERCHANT, new MerchantInventoryTranslator()); + put(WindowType.SHULKER_BOX, new ShulkerInventoryTranslator()); + put(WindowType.SMITHING, new SmithingInventoryTranslator()); + + /* Generics */ put(WindowType.GENERIC_3X3, new GenericBlockInventoryTranslator(9, "minecraft:dispenser[facing=north,triggered=false]", ContainerType.DISPENSER)); put(WindowType.HOPPER, new GenericBlockInventoryTranslator(5, "minecraft:hopper[enabled=false,facing=down]", ContainerType.HOPPER)); - //put(WindowType.BEACON, new AbstractBlockInventoryTranslator(1, "minecraft:beacon", ContainerType.BEACON)); //TODO*/ + //put(WindowType.BEACON, new AbstractBlockInventoryTranslator(1, "minecraft:beacon", ContainerType.BEACON)); //TODO //put(WindowType.CARTOGRAPHY //put(WindowType.STONECUTTER @@ -110,12 +117,30 @@ public abstract class InventoryTranslator { public abstract SlotType getSlotType(int javaSlot); public abstract Inventory createInventory(String name, int windowId, WindowType windowType, PlayerInventory playerInventory); + /** + * Should be overrided if this request matches a certain criteria and shouldn't be treated normally. + * E.G. anvil renaming or enchanting + */ + public boolean shouldHandleRequestFirst(StackRequestActionData action) { + return false; + } + + /** + * If {@link #shouldHandleRequestFirst(StackRequestActionData)} returns true, this will be called + */ + public ItemStackResponsePacket.Response translateSpecialRequest(GeyserSession session, Inventory inventory, ItemStackRequestPacket.Request request) { + return null; + } + public void translateRequests(GeyserSession session, Inventory inventory, List requests) { ItemStackResponsePacket responsePacket = new ItemStackResponsePacket(); for (ItemStackRequestPacket.Request request : requests) { if (request.getActions().length > 0) { StackRequestActionData firstAction = request.getActions()[0]; - if (firstAction.getType() == StackRequestActionType.CRAFT_RECIPE || firstAction.getType() == StackRequestActionType.CRAFT_RECIPE_AUTO) { + if (shouldHandleRequestFirst(firstAction)) { + // Some special request that shouldn't be processed normally + responsePacket.getEntries().add(translateSpecialRequest(session, inventory, request)); + } else if (firstAction.getType() == StackRequestActionType.CRAFT_RECIPE || firstAction.getType() == StackRequestActionType.CRAFT_RECIPE_AUTO) { responsePacket.getEntries().add(translateCraftingRequest(session, inventory, request)); } else if (firstAction.getType() == StackRequestActionType.CRAFT_CREATIVE) { // This is also used for pulling items out of creative @@ -335,12 +360,30 @@ public abstract class InventoryTranslator { return rejectRequest(request); if (isCursor(dropAction.getSource())) { //clicking outside of window - int sourceAmount = plan.getCursor().getAmount(); - if (dropAction.getCount() == sourceAmount) { //drop all - plan.add(Click.LEFT_OUTSIDE, Click.OUTSIDE_SLOT); - } else { //drop some - for (int i = 0; i < dropAction.getCount(); i++) { - plan.add(Click.RIGHT_OUTSIDE, Click.OUTSIDE_SLOT); //drop one until goal is met + if (session.getGameMode() == GameMode.CREATIVE && inventory instanceof PlayerInventory) { + GeyserItemStack cursorItem = session.getPlayerInventory().getCursor(); + GeyserItemStack droppingItem = cursorItem.copy(); + // Subtract the cursor item by however much is being dropped + cursorItem.setAmount(cursorItem.getAmount() - dropAction.getCount()); + if (cursorItem.isEmpty()) { + // Cursor item no longer exists + session.getPlayerInventory().setCursor(GeyserItemStack.EMPTY); + } + droppingItem.setAmount(dropAction.getCount()); + ClientCreativeInventoryActionPacket packet = new ClientCreativeInventoryActionPacket( + Click.OUTSIDE_SLOT, + droppingItem.getItemStack() + ); + System.out.println(packet.toString()); + session.sendDownstreamPacket(packet); + } else { + int sourceAmount = plan.getCursor().getAmount(); + if (dropAction.getCount() == sourceAmount) { //drop all + plan.add(Click.LEFT_OUTSIDE, Click.OUTSIDE_SLOT); + } else { //drop some + for (int i = 0; i < dropAction.getCount(); i++) { + plan.add(Click.RIGHT_OUTSIDE, Click.OUTSIDE_SLOT); //drop one until goal is met + } } } } else { //dropping from inventory @@ -395,6 +438,10 @@ public abstract class InventoryTranslator { case CRAFT_RESULTS_DEPRECATED: { break; } + case CRAFT_RECIPE_OPTIONAL: { + // Anvils and cartography tables will handle this + break; + } default: return rejectRequest(request); } @@ -578,7 +625,14 @@ public abstract class InventoryTranslator { Collections.singletonList(makeItemEntry(session, 0, session.getPlayerInventory().getCursor()))))); } else { int javaSlot = bedrockSlotToJava(transferAction.getDestination()); - inventory.setItem(javaSlot, GeyserItemStack.from(javaCreativeItem, session.getItemNetId().getAndIncrement())); + GeyserItemStack existingItem = inventory.getItem(javaSlot); + if (existingItem.getId() == javaCreativeItem.getId()) { + // Adding more to an existing item + existingItem.setAmount(existingItem.getAmount() + transferAction.getCount()); + javaCreativeItem = existingItem.getItemStack(); + } else { + inventory.setItem(javaSlot, GeyserItemStack.from(javaCreativeItem, session.getItemNetId().getAndIncrement())); + } ClientCreativeInventoryActionPacket creativeActionPacket = new ClientCreativeInventoryActionPacket( javaSlot, javaCreativeItem diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/AnvilInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/AnvilInventoryTranslator.java new file mode 100644 index 00000000..27de329c --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/AnvilInventoryTranslator.java @@ -0,0 +1,87 @@ +/* + * 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.translators; + +import com.github.steveice10.mc.protocol.data.game.window.WindowType; +import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; +import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; +import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData; +import org.geysermc.connector.inventory.AnvilContainer; +import org.geysermc.connector.inventory.Inventory; +import org.geysermc.connector.inventory.PlayerInventory; +import org.geysermc.connector.network.translators.inventory.BedrockContainerSlot; +import org.geysermc.connector.network.translators.inventory.updater.UIInventoryUpdater; + +public class AnvilInventoryTranslator extends AbstractBlockInventoryTranslator { + public AnvilInventoryTranslator() { + super(3, "minecraft:anvil[facing=north]", ContainerType.ANVIL, UIInventoryUpdater.INSTANCE); + } + + @Override + public int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData) { + if (slotInfoData.getContainer() == ContainerSlotType.ANVIL_INPUT) { + return 0; + } + if (slotInfoData.getContainer() == ContainerSlotType.ANVIL_MATERIAL) { + return 1; + } + if (slotInfoData.getContainer() == ContainerSlotType.ANVIL_RESULT || slotInfoData.getContainer() == ContainerSlotType.CREATIVE_OUTPUT) { + return 2; + } + return super.bedrockSlotToJava(slotInfoData); + } + + @Override + public BedrockContainerSlot javaSlotToBedrockContainer(int slot) { + switch (slot) { + case 0: + return new BedrockContainerSlot(ContainerSlotType.ANVIL_INPUT, 1); + case 1: + return new BedrockContainerSlot(ContainerSlotType.ANVIL_MATERIAL, 2); + case 2: + return new BedrockContainerSlot(ContainerSlotType.ANVIL_RESULT, 50); + } + return super.javaSlotToBedrockContainer(slot); + } + + @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 Inventory createInventory(String name, int windowId, WindowType windowType, PlayerInventory playerInventory) { + return new AnvilContainer(name, windowId, windowType, this.size, playerInventory); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/CraftingInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/CraftingInventoryTranslator.java index 99ed2ece..77eeee17 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/CraftingInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/CraftingInventoryTranslator.java @@ -30,11 +30,11 @@ import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData; import org.geysermc.connector.network.translators.inventory.BedrockContainerSlot; import org.geysermc.connector.network.translators.inventory.SlotType; -import org.geysermc.connector.network.translators.inventory.updater.CursorInventoryUpdater; +import org.geysermc.connector.network.translators.inventory.updater.UIInventoryUpdater; public class CraftingInventoryTranslator extends AbstractBlockInventoryTranslator { public CraftingInventoryTranslator() { - super(10, "minecraft:crafting_table", ContainerType.WORKBENCH, CursorInventoryUpdater.INSTANCE); + super(10, "minecraft:crafting_table", ContainerType.WORKBENCH, UIInventoryUpdater.INSTANCE); } @Override diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/EnchantingInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/EnchantingInventoryTranslator.java index a8b13be4..cfa02419 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/EnchantingInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/EnchantingInventoryTranslator.java @@ -25,11 +25,195 @@ package org.geysermc.connector.network.translators.inventory.translators; +import com.github.steveice10.mc.protocol.data.game.window.WindowType; +import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientClickWindowButtonPacket; +import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; -import org.geysermc.connector.network.translators.inventory.updater.CursorInventoryUpdater; +import com.nukkitx.protocol.bedrock.data.inventory.EnchantOptionData; +import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData; +import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.CraftRecipeStackRequestActionData; +import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.StackRequestActionData; +import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.StackRequestActionType; +import com.nukkitx.protocol.bedrock.packet.ItemStackRequestPacket; +import com.nukkitx.protocol.bedrock.packet.ItemStackResponsePacket; +import com.nukkitx.protocol.bedrock.packet.PlayerEnchantOptionsPacket; +import org.geysermc.connector.inventory.EnchantingContainer; +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.BedrockContainerSlot; +import org.geysermc.connector.network.translators.inventory.updater.UIInventoryUpdater; +import org.geysermc.connector.network.translators.item.Enchantment; + +import java.util.Arrays; +import java.util.Collections; public class EnchantingInventoryTranslator extends AbstractBlockInventoryTranslator { public EnchantingInventoryTranslator() { - super(2, "minecraft:enchanting_table", ContainerType.ENCHANTMENT, CursorInventoryUpdater.INSTANCE); + super(2, "minecraft:enchanting_table", ContainerType.ENCHANTMENT, UIInventoryUpdater.INSTANCE); + } + + @Override + public void updateProperty(GeyserSession session, Inventory inventory, int key, int value) { + int slotToUpdate; + EnchantingContainer enchantingInventory = (EnchantingContainer) inventory; + boolean shouldUpdate = false; + switch (key) { + case 0: + case 1: + case 2: + // Experience required + slotToUpdate = key; + enchantingInventory.getGeyserEnchantOptions()[slotToUpdate].setXpCost(value); + break; + case 4: + case 5: + case 6: + // Enchantment type + slotToUpdate = key - 4; + int index = value; + if (index != -1) { + Enchantment enchantment = Enchantment.getByJavaIdentifier("minecraft:" + JavaEnchantment.values()[index].name().toLowerCase()); + if (enchantment != null) { + // Convert the Java enchantment index to Bedrock's + index = enchantment.ordinal(); + } else { + index = -1; + } + } + enchantingInventory.getGeyserEnchantOptions()[slotToUpdate].setJavaEnchantIndex(value); + enchantingInventory.getGeyserEnchantOptions()[slotToUpdate].setBedrockEnchantIndex(index); + break; + case 7: + case 8: + case 9: + // Enchantment level + slotToUpdate = key - 7; + enchantingInventory.getGeyserEnchantOptions()[slotToUpdate].setEnchantLevel(value); + shouldUpdate = true; // Java sends each property as its own packet, so let's only update after all properties have been sent + break; + default: + return; + } + if (shouldUpdate) { + enchantingInventory.getEnchantOptions()[slotToUpdate] = enchantingInventory.getGeyserEnchantOptions()[slotToUpdate].build(session); + PlayerEnchantOptionsPacket packet = new PlayerEnchantOptionsPacket(); + packet.getOptions().addAll(Arrays.asList(enchantingInventory.getEnchantOptions())); + System.out.println(packet); + session.sendUpstreamPacket(packet); + } + } + + @Override + public boolean shouldHandleRequestFirst(StackRequestActionData action) { + return action.getType() == StackRequestActionType.CRAFT_RECIPE; + } + + @Override + public ItemStackResponsePacket.Response translateSpecialRequest(GeyserSession session, Inventory inventory, ItemStackRequestPacket.Request request) { + // Client has requested an item to be enchanted + CraftRecipeStackRequestActionData craftRecipeData = (CraftRecipeStackRequestActionData) request.getActions()[0]; + EnchantingContainer enchantingInventory = (EnchantingContainer) inventory; + int javaSlot = -1; + for (int i = 0; i < enchantingInventory.getEnchantOptions().length; i++) { + EnchantOptionData enchantData = enchantingInventory.getEnchantOptions()[i]; + if (enchantData != null) { + if (craftRecipeData.getRecipeNetworkId() == enchantData.getEnchantNetId()) { + // Enchant net ID is how we differentiate between what item Bedrock wants + javaSlot = enchantingInventory.getGeyserEnchantOptions()[i].getJavaIndex(); + break; + } + } + } + if (javaSlot == -1) { + // Slot should be determined as 0, 1, or 2 + throw new RuntimeException("Cannot find enchant slot for item!"); + } + ClientClickWindowButtonPacket packet = new ClientClickWindowButtonPacket(inventory.getId(), javaSlot); + System.out.println(packet); + session.sendDownstreamPacket(packet); + return acceptRequest(request, makeContainerEntries(session, inventory, Collections.emptySet())); + } + + @Override + public int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData) { + if (slotInfoData.getContainer() == ContainerSlotType.ENCHANTING_INPUT) { + return 0; + } + if (slotInfoData.getContainer() == ContainerSlotType.ENCHANTING_LAPIS) { + return 1; + } + return super.bedrockSlotToJava(slotInfoData); + } + + @Override + public BedrockContainerSlot javaSlotToBedrockContainer(int slot) { + if (slot == 0) { + return new BedrockContainerSlot(ContainerSlotType.ENCHANTING_INPUT, 14); + } + if (slot == 1) { + return new BedrockContainerSlot(ContainerSlotType.ENCHANTING_LAPIS, 15); + } + return super.javaSlotToBedrockContainer(slot); + } + + @Override + public int javaSlotToBedrock(int slot) { + if (slot == 0) { + return 14; + } + if (slot == 1) { + return 15; + } + return super.javaSlotToBedrock(slot); + } + + @Override + public Inventory createInventory(String name, int windowId, WindowType windowType, PlayerInventory playerInventory) { + return new EnchantingContainer(name, windowId, windowType, this.size, playerInventory); + } + + /** + * Enchantments classified by their Java index + */ + public enum JavaEnchantment { + PROTECTION, + FIRE_PROTECTION, + FEATHER_FALLING, + BLAST_PROTECTION, + PROJECTILE_PROTECTION, + RESPIRATION, + AQUA_AFFINITY, + THORNS, + DEPTH_STRIDER, + FROST_WALKER, + BINDING_CURSE, + SOUL_SPEED, + SHARPNESS, + SMITE, + BANE_OF_ARTHROPODS, + KNOCKBACK, + FIRE_ASPECT, + LOOTING, + SWEEPING, + EFFICIENCY, + SILK_TOUCH, + UNBREAKING, + FORTUNE, + POWER, + PUNCH, + FLAME, + INFINITY, + LUCK_OF_THE_SEA, + LURE, + LOYALTY, + IMPALING, + RIPTIDE, + CHANNELING, + MULTISHOT, + QUICK_CHARGE, + PIERCING, + MENDING, + VANISHING_CURSE } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/GrindstoneInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/GrindstoneInventoryTranslator.java index 684b25e8..14e3cf1e 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/GrindstoneInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/GrindstoneInventoryTranslator.java @@ -29,11 +29,11 @@ import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData; import org.geysermc.connector.network.translators.inventory.BedrockContainerSlot; -import org.geysermc.connector.network.translators.inventory.updater.CursorInventoryUpdater; +import org.geysermc.connector.network.translators.inventory.updater.UIInventoryUpdater; public class GrindstoneInventoryTranslator extends AbstractBlockInventoryTranslator { public GrindstoneInventoryTranslator() { - super(3, "minecraft:grindstone[face=floor,facing=north]", ContainerType.GRINDSTONE, CursorInventoryUpdater.INSTANCE); + super(3, "minecraft:grindstone[face=floor,facing=north]", ContainerType.GRINDSTONE, UIInventoryUpdater.INSTANCE); } @Override diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/MerchantInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/MerchantInventoryTranslator.java index e798157d..33a4e824 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/MerchantInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/MerchantInventoryTranslator.java @@ -44,12 +44,11 @@ import org.geysermc.connector.network.translators.inventory.updater.InventoryUpd import org.geysermc.connector.network.translators.inventory.updater.UIInventoryUpdater; public class MerchantInventoryTranslator extends BaseInventoryTranslator { - private final InventoryUpdater updater; public MerchantInventoryTranslator() { super(3); - this.updater = new UIInventoryUpdater(); + this.updater = UIInventoryUpdater.INSTANCE; } @Override diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/SmithingInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/SmithingInventoryTranslator.java index fba8a8d6..4d708bc3 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/SmithingInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/SmithingInventoryTranslator.java @@ -29,11 +29,11 @@ import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData; import org.geysermc.connector.network.translators.inventory.BedrockContainerSlot; -import org.geysermc.connector.network.translators.inventory.updater.CursorInventoryUpdater; +import org.geysermc.connector.network.translators.inventory.updater.UIInventoryUpdater; public class SmithingInventoryTranslator extends AbstractBlockInventoryTranslator { public SmithingInventoryTranslator() { - super(3, "minecraft:smithing_table", ContainerType.SMITHING_TABLE, CursorInventoryUpdater.INSTANCE); + super(3, "minecraft:smithing_table", ContainerType.SMITHING_TABLE, UIInventoryUpdater.INSTANCE); } @Override 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 deleted file mode 100644 index 5d71f7e0..00000000 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/CursorInventoryUpdater.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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.inventory.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.inventory.InventoryTranslator; - -public class CursorInventoryUpdater extends InventoryUpdater { - public static final CursorInventoryUpdater INSTANCE = new CursorInventoryUpdater(); - - @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.UI); - slotPacket.setSlot(bedrockSlot); - slotPacket.setItem(inventory.getItem(i).getItemData(session)); - session.sendUpstreamPacket(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.UI); - slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot)); - slotPacket.setItem(inventory.getItem(javaSlot).getItemData(session)); - session.sendUpstreamPacket(slotPacket); - return true; - } -} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/UIInventoryUpdater.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/UIInventoryUpdater.java index 5100ddc9..1ebad489 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/UIInventoryUpdater.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/UIInventoryUpdater.java @@ -30,9 +30,9 @@ 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.inventory.InventoryTranslator; -import org.geysermc.connector.network.translators.item.ItemTranslator; public class UIInventoryUpdater extends InventoryUpdater { + public static final UIInventoryUpdater INSTANCE = new UIInventoryUpdater(); @Override public void updateInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) { 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 97c4708f..12043936 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 @@ -38,6 +38,7 @@ public class JavaWindowPropertyTranslator extends PacketTranslator { Inventory inventory = InventoryUtils.getInventory(session, packet.getWindowId()); if (inventory == null) From ff4f712eda405ff1b0e51a62f0d062d78695e024 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 23 Dec 2020 12:30:36 -0500 Subject: [PATCH 11/39] Implement beacon --- .../connector/inventory/BeaconContainer.java | 41 ++++++ .../connector/inventory/Inventory.java | 3 + .../inventory/InventoryTranslator.java | 2 +- .../holder/BlockInventoryHolder.java | 8 ++ .../BeaconInventoryTranslator.java | 134 ++++++++++++++++++ .../entity/BeaconBlockEntityTranslator.java | 41 ++++++ 6 files changed, 228 insertions(+), 1 deletion(-) create mode 100644 connector/src/main/java/org/geysermc/connector/inventory/BeaconContainer.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/BeaconInventoryTranslator.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BeaconBlockEntityTranslator.java diff --git a/connector/src/main/java/org/geysermc/connector/inventory/BeaconContainer.java b/connector/src/main/java/org/geysermc/connector/inventory/BeaconContainer.java new file mode 100644 index 00000000..70ecd84f --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/inventory/BeaconContainer.java @@ -0,0 +1,41 @@ +/* + * 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.inventory; + +import com.github.steveice10.mc.protocol.data.game.window.WindowType; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class BeaconContainer extends Container { + private int primaryId; + private int secondaryId; + + public BeaconContainer(String title, int id, WindowType windowType, int size, PlayerInventory playerInventory) { + super(title, id, windowType, size, playerInventory); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/inventory/Inventory.java b/connector/src/main/java/org/geysermc/connector/inventory/Inventory.java index f4aea4c9..fed94b0b 100644 --- a/connector/src/main/java/org/geysermc/connector/inventory/Inventory.java +++ b/connector/src/main/java/org/geysermc/connector/inventory/Inventory.java @@ -50,6 +50,9 @@ public class Inventory { protected GeyserItemStack[] items; + /** + * The location of the inventory block. Will either be a fake block above the player's head, or the actual block location + */ @Getter @Setter protected Vector3i holderPosition = Vector3i.ZERO; 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 719a2092..a726e2d7 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 @@ -80,6 +80,7 @@ public abstract class InventoryTranslator { /* Specific Inventories */ put(WindowType.ANVIL, new AnvilInventoryTranslator()); + put(WindowType.BEACON, new BeaconInventoryTranslator()); put(WindowType.BREWING_STAND, new BrewingInventoryTranslator()); put(WindowType.CRAFTING, new CraftingInventoryTranslator()); put(WindowType.ENCHANTMENT, new EnchantingInventoryTranslator()); @@ -91,7 +92,6 @@ public abstract class InventoryTranslator { /* Generics */ put(WindowType.GENERIC_3X3, new GenericBlockInventoryTranslator(9, "minecraft:dispenser[facing=north,triggered=false]", ContainerType.DISPENSER)); put(WindowType.HOPPER, new GenericBlockInventoryTranslator(5, "minecraft:hopper[enabled=false,facing=down]", ContainerType.HOPPER)); - //put(WindowType.BEACON, new AbstractBlockInventoryTranslator(1, "minecraft:beacon", ContainerType.BEACON)); //TODO //put(WindowType.CARTOGRAPHY //put(WindowType.STONECUTTER 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 index 339c8a54..1b4dfb8d 100644 --- 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 @@ -48,6 +48,11 @@ public class BlockInventoryHolder extends InventoryHolder { @Override public void prepareInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) { + //TODO: Improve on this (for example, multiple block states). We need this for the beacon. + if (BlockTranslator.getBedrockBlockId(session.getConnector().getWorldManager().getBlockAt(session, session.getLastInteractionPosition())) == blockId) { + inventory.setHolderPosition(session.getLastInteractionPosition()); + return; + } Vector3i position = session.getPlayerEntity().getPosition().toInt(); position = position.add(Vector3i.UP); UpdateBlockPacket blockPacket = new UpdateBlockPacket(); @@ -82,6 +87,9 @@ public class BlockInventoryHolder extends InventoryHolder { @Override public void closeInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) { Vector3i holderPos = inventory.getHolderPosition(); + if (holderPos.equals(session.getLastInteractionPosition())) { + return; + } Position pos = new Position(holderPos.getX(), holderPos.getY(), holderPos.getZ()); int realBlock = session.getConnector().getWorldManager().getBlockAt(session, pos.getX(), pos.getY(), pos.getZ()); UpdateBlockPacket blockPacket = new UpdateBlockPacket(); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/BeaconInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/BeaconInventoryTranslator.java new file mode 100644 index 00000000..678d7e84 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/BeaconInventoryTranslator.java @@ -0,0 +1,134 @@ +/* + * 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.translators; + +import com.github.steveice10.mc.protocol.data.game.window.WindowType; +import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientSetBeaconEffectPacket; +import com.nukkitx.math.vector.Vector3i; +import com.nukkitx.nbt.NbtMap; +import com.nukkitx.nbt.NbtMapBuilder; +import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; +import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; +import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData; +import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.BeaconPaymentStackRequestActionData; +import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.StackRequestActionData; +import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.StackRequestActionType; +import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket; +import com.nukkitx.protocol.bedrock.packet.ItemStackRequestPacket; +import com.nukkitx.protocol.bedrock.packet.ItemStackResponsePacket; +import org.geysermc.connector.inventory.BeaconContainer; +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.BedrockContainerSlot; +import org.geysermc.connector.network.translators.inventory.updater.UIInventoryUpdater; + +import java.util.Collections; + +public class BeaconInventoryTranslator extends AbstractBlockInventoryTranslator { + public BeaconInventoryTranslator() { + super(1, "minecraft:beacon", ContainerType.BEACON, UIInventoryUpdater.INSTANCE); + } + + @Override + public void updateProperty(GeyserSession session, Inventory inventory, int key, int value) { + //FIXME?: Beacon graphics look weird after inputting an item. This might be a Bedrock bug, since it resets to nothing + // on BDS + BeaconContainer beaconContainer = (BeaconContainer) inventory; + switch (key) { + case 0: + // Power - beacon doesn't use this, and uses the block position instead + break; + case 1: + beaconContainer.setPrimaryId(value == -1 ? 0 : value); + break; + case 2: + beaconContainer.setSecondaryId(value == -1 ? 0 : value); + break; + } + + // Send a block entity data packet update to the fake beacon inventory + Vector3i position = inventory.getHolderPosition(); + NbtMapBuilder builder = NbtMap.builder() + .putInt("x", position.getX()) + .putInt("y", position.getY()) + .putInt("z", position.getZ()) + .putString("CustomName", inventory.getTitle()) + .putString("id", "Beacon") + .putInt("primary", beaconContainer.getPrimaryId()) + .putInt("secondary", beaconContainer.getSecondaryId()); + + BlockEntityDataPacket packet = new BlockEntityDataPacket(); + packet.setBlockPosition(position); + packet.setData(builder.build()); + System.out.println(packet.toString()); + session.sendUpstreamPacket(packet); + } + + @Override + public boolean shouldHandleRequestFirst(StackRequestActionData action) { + return action.getType() == StackRequestActionType.BEACON_PAYMENT; + } + + @Override + public ItemStackResponsePacket.Response translateSpecialRequest(GeyserSession session, Inventory inventory, ItemStackRequestPacket.Request request) { + // Input a beacon payment + BeaconPaymentStackRequestActionData beaconPayment = (BeaconPaymentStackRequestActionData) request.getActions()[0]; + ClientSetBeaconEffectPacket packet = new ClientSetBeaconEffectPacket(beaconPayment.getPrimaryEffect(), beaconPayment.getSecondaryEffect()); + System.out.println(packet.toString()); + session.sendDownstreamPacket(packet); + return acceptRequest(request, makeContainerEntries(session, inventory, Collections.emptySet())); + } + + @Override + public int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData) { + if (slotInfoData.getContainer() == ContainerSlotType.BEACON_PAYMENT) { + return 0; + } + return super.bedrockSlotToJava(slotInfoData); + } + + @Override + public BedrockContainerSlot javaSlotToBedrockContainer(int slot) { + if (slot == 0) { + return new BedrockContainerSlot(ContainerSlotType.BEACON_PAYMENT, 27); + } + return super.javaSlotToBedrockContainer(slot); + } + + @Override + public int javaSlotToBedrock(int slot) { + if (slot == 0) { + return 27; + } + return super.javaSlotToBedrock(slot); + } + + @Override + public Inventory createInventory(String name, int windowId, WindowType windowType, PlayerInventory playerInventory) { + return new BeaconContainer(name, windowId, windowType, this.size, playerInventory); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BeaconBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BeaconBlockEntityTranslator.java new file mode 100644 index 00000000..fca6af9b --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BeaconBlockEntityTranslator.java @@ -0,0 +1,41 @@ +/* + * 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.world.block.entity; + +import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import com.nukkitx.nbt.NbtMapBuilder; + +@BlockEntity(name = "Beacon", regex = "beacon") +public class BeaconBlockEntityTranslator extends BlockEntityTranslator { + @Override + public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) { + int primary = getOrDefault(tag.get("Primary"), 0); + // The effects here generally map one-to-one Java <-> Bedrock. Only the newer ones get more complicated + builder.putInt("primary", primary == -1 ? 0 : primary); + int secondary = getOrDefault(tag.get("Secondary"), 0); + builder.putInt("secondary", secondary == -1 ? 0 : secondary); + } +} From 0f735a833050809b0e9b238111da49a176b570f5 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 23 Dec 2020 12:37:55 -0500 Subject: [PATCH 12/39] Block entity cleanup --- .../world/block/entity/BannerBlockEntityTranslator.java | 2 +- .../world/block/entity/BeaconBlockEntityTranslator.java | 2 +- .../world/block/entity/BedBlockEntityTranslator.java | 2 +- .../translators/world/block/entity/BlockEntity.java | 6 ------ .../world/block/entity/BlockEntityTranslator.java | 7 +++---- .../world/block/entity/CampfireBlockEntityTranslator.java | 2 +- .../block/entity/CommandBlockBlockEntityTranslator.java | 2 +- .../block/entity/DoubleChestBlockEntityTranslator.java | 4 ++-- .../world/block/entity/EmptyBlockEntityTranslator.java | 2 +- .../block/entity/EndGatewayBlockEntityTranslator.java | 2 +- .../block/entity/JigsawBlockBlockEntityTranslator.java | 2 +- .../block/entity/ShulkerBoxBlockEntityTranslator.java | 2 +- .../world/block/entity/SignBlockEntityTranslator.java | 2 +- .../world/block/entity/SkullBlockEntityTranslator.java | 2 +- .../world/block/entity/SpawnerBlockEntityTranslator.java | 2 +- 15 files changed, 17 insertions(+), 24 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BannerBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BannerBlockEntityTranslator.java index b5979479..18209508 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BannerBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BannerBlockEntityTranslator.java @@ -31,7 +31,7 @@ import com.nukkitx.nbt.NbtMapBuilder; import org.geysermc.connector.network.translators.item.translators.BannerTranslator; import org.geysermc.connector.network.translators.world.block.BlockStateValues; -@BlockEntity(name = "Banner", regex = "banner") +@BlockEntity(name = "Banner") public class BannerBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState { @Override public boolean isBlock(int blockState) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BeaconBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BeaconBlockEntityTranslator.java index fca6af9b..f60b0daa 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BeaconBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BeaconBlockEntityTranslator.java @@ -28,7 +28,7 @@ package org.geysermc.connector.network.translators.world.block.entity; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.nukkitx.nbt.NbtMapBuilder; -@BlockEntity(name = "Beacon", regex = "beacon") +@BlockEntity(name = "Beacon") public class BeaconBlockEntityTranslator extends BlockEntityTranslator { @Override public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BedBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BedBlockEntityTranslator.java index 0067cc41..a111274d 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BedBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BedBlockEntityTranslator.java @@ -29,7 +29,7 @@ import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.nukkitx.nbt.NbtMapBuilder; import org.geysermc.connector.network.translators.world.block.BlockStateValues; -@BlockEntity(name = "Bed", regex = "bed") +@BlockEntity(name = "Bed") public class BedBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState { @Override public boolean isBlock(int blockState) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BlockEntity.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BlockEntity.java index 11bfe0ea..3a1345ac 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BlockEntity.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BlockEntity.java @@ -36,10 +36,4 @@ public @interface BlockEntity { * @return the name of the block entity */ String name(); - - /** - * The search term used in BlockTranslator - * @return the search term used in BlockTranslator - */ - String regex(); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BlockEntityTranslator.java index 67963652..b9373b3c 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BlockEntityTranslator.java @@ -109,7 +109,7 @@ public abstract class BlockEntityTranslator { int y = ((IntTag) tag.getValue().get("y")).getValue(); int z = ((IntTag) tag.getValue().get("z")).getValue(); - NbtMapBuilder tagBuilder = getConstantBedrockTag(BlockEntityUtils.getBedrockBlockEntityId(id), x, y, z).toBuilder(); + NbtMapBuilder tagBuilder = getConstantBedrockTag(BlockEntityUtils.getBedrockBlockEntityId(id), x, y, z); translateTag(tagBuilder, tag, blockState); return tagBuilder.build(); } @@ -123,13 +123,12 @@ public abstract class BlockEntityTranslator { return tag; } - protected NbtMap getConstantBedrockTag(String bedrockId, int x, int y, int z) { + protected NbtMapBuilder getConstantBedrockTag(String bedrockId, int x, int y, int z) { return NbtMap.builder() .putInt("x", x) .putInt("y", y) .putInt("z", z) - .putString("id", bedrockId) - .build(); + .putString("id", bedrockId); } @SuppressWarnings("unchecked") diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/CampfireBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/CampfireBlockEntityTranslator.java index 3e4f9fb9..e41bc5ff 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/CampfireBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/CampfireBlockEntityTranslator.java @@ -32,7 +32,7 @@ import com.nukkitx.nbt.NbtMapBuilder; import org.geysermc.connector.network.translators.item.ItemEntry; import org.geysermc.connector.network.translators.item.ItemRegistry; -@BlockEntity(name = "Campfire", regex = "campfire") +@BlockEntity(name = "Campfire") public class CampfireBlockEntityTranslator extends BlockEntityTranslator { @Override public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/CommandBlockBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/CommandBlockBlockEntityTranslator.java index 1eb50ffe..99e89f27 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/CommandBlockBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/CommandBlockBlockEntityTranslator.java @@ -30,7 +30,7 @@ import com.nukkitx.nbt.NbtMapBuilder; import org.geysermc.connector.network.translators.world.block.BlockStateValues; import org.geysermc.connector.network.translators.chat.MessageTranslator; -@BlockEntity(name = "CommandBlock", regex = "command_block") +@BlockEntity(name = "CommandBlock") public class CommandBlockBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState { @Override public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/DoubleChestBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/DoubleChestBlockEntityTranslator.java index 47bcf489..a5b67ecb 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/DoubleChestBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/DoubleChestBlockEntityTranslator.java @@ -36,7 +36,7 @@ import org.geysermc.connector.utils.BlockEntityUtils; /** * Chests have more block entity properties in Bedrock, which is solved by implementing the BedrockOnlyBlockEntity */ -@BlockEntity(name = "Chest", regex = "chest") +@BlockEntity(name = "Chest") public class DoubleChestBlockEntityTranslator extends BlockEntityTranslator implements BedrockOnlyBlockEntity, RequiresBlockState { @Override public boolean isBlock(int blockState) { @@ -46,7 +46,7 @@ public class DoubleChestBlockEntityTranslator extends BlockEntityTranslator impl @Override public void updateBlock(GeyserSession session, int blockState, Vector3i position) { CompoundTag javaTag = getConstantJavaTag("chest", position.getX(), position.getY(), position.getZ()); - NbtMapBuilder tagBuilder = getConstantBedrockTag(BlockEntityUtils.getBedrockBlockEntityId("chest"), position.getX(), position.getY(), position.getZ()).toBuilder(); + NbtMapBuilder tagBuilder = getConstantBedrockTag(BlockEntityUtils.getBedrockBlockEntityId("chest"), position.getX(), position.getY(), position.getZ()); translateTag(tagBuilder, javaTag, blockState); BlockEntityUtils.updateBlockEntity(session, tagBuilder.build(), position); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/EmptyBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/EmptyBlockEntityTranslator.java index 3926b866..0b883905 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/EmptyBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/EmptyBlockEntityTranslator.java @@ -28,7 +28,7 @@ package org.geysermc.connector.network.translators.world.block.entity; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.nukkitx.nbt.NbtMapBuilder; -@BlockEntity(name = "Empty", regex = "") +@BlockEntity(name = "Empty") public class EmptyBlockEntityTranslator extends BlockEntityTranslator { @Override public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/EndGatewayBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/EndGatewayBlockEntityTranslator.java index 0bf58822..03689ac9 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/EndGatewayBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/EndGatewayBlockEntityTranslator.java @@ -35,7 +35,7 @@ import it.unimi.dsi.fastutil.ints.IntList; import java.util.LinkedHashMap; -@BlockEntity(name = "EndGateway", regex = "end_gateway") +@BlockEntity(name = "EndGateway") public class EndGatewayBlockEntityTranslator extends BlockEntityTranslator { @Override public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/JigsawBlockBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/JigsawBlockBlockEntityTranslator.java index 4fcdfe54..6f3e7b3d 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/JigsawBlockBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/JigsawBlockBlockEntityTranslator.java @@ -29,7 +29,7 @@ import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.StringTag; import com.nukkitx.nbt.NbtMapBuilder; -@BlockEntity(name = "JigsawBlock", regex = "jigsaw") +@BlockEntity(name = "JigsawBlock") public class JigsawBlockBlockEntityTranslator extends BlockEntityTranslator { @Override public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/ShulkerBoxBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/ShulkerBoxBlockEntityTranslator.java index 69fa1084..0eb27da5 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/ShulkerBoxBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/ShulkerBoxBlockEntityTranslator.java @@ -29,7 +29,7 @@ import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.nukkitx.nbt.NbtMapBuilder; import org.geysermc.connector.network.translators.world.block.BlockStateValues; -@BlockEntity(name = "ShulkerBox", regex = "shulker_box") +@BlockEntity(name = "ShulkerBox") public class ShulkerBoxBlockEntityTranslator extends BlockEntityTranslator { @Override public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SignBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SignBlockEntityTranslator.java index a9641d77..31397086 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SignBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SignBlockEntityTranslator.java @@ -30,7 +30,7 @@ import com.nukkitx.nbt.NbtMapBuilder; import org.geysermc.connector.network.translators.chat.MessageTranslator; import org.geysermc.connector.utils.SignUtils; -@BlockEntity(name = "Sign", regex = "sign") +@BlockEntity(name = "Sign") public class SignBlockEntityTranslator extends BlockEntityTranslator { /** * Maps a color stored in a sign's Color tag to a Bedrock Edition formatting code. diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SkullBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SkullBlockEntityTranslator.java index 5da9c0e0..d8249ac5 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SkullBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SkullBlockEntityTranslator.java @@ -46,7 +46,7 @@ import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; -@BlockEntity(name = "Skull", regex = "skull") +@BlockEntity(name = "Skull") public class SkullBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState { public static boolean ALLOW_CUSTOM_SKULLS; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SpawnerBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SpawnerBlockEntityTranslator.java index 38507f54..4b8b344a 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SpawnerBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SpawnerBlockEntityTranslator.java @@ -30,7 +30,7 @@ import com.github.steveice10.opennbt.tag.builtin.Tag; import com.nukkitx.nbt.NbtMapBuilder; import org.geysermc.connector.entity.type.EntityType; -@BlockEntity(name = "MobSpawner", regex = "mob_spawner") +@BlockEntity(name = "MobSpawner") public class SpawnerBlockEntityTranslator extends BlockEntityTranslator { @Override public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) { From 4d80edf6d9139914a5233493325f731038c93aba Mon Sep 17 00:00:00 2001 From: D3ATHBRINGER13 Date: Thu, 24 Dec 2020 00:48:21 +0000 Subject: [PATCH 13/39] Initial loom funtionality --- .../inventory/InventoryTranslator.java | 3 +- .../translators/LoomInventoryTranslator.java | 85 +++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LoomInventoryTranslator.java 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 a726e2d7..1dc8fa7d 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 @@ -93,9 +93,10 @@ public abstract class InventoryTranslator { put(WindowType.GENERIC_3X3, new GenericBlockInventoryTranslator(9, "minecraft:dispenser[facing=north,triggered=false]", ContainerType.DISPENSER)); put(WindowType.HOPPER, new GenericBlockInventoryTranslator(5, "minecraft:hopper[enabled=false,facing=down]", ContainerType.HOPPER)); + /* Workstations */ //put(WindowType.CARTOGRAPHY //put(WindowType.STONECUTTER - //put(WindowType.LOOM + put(WindowType.LOOM, new LoomInventoryTranslator()); //put(WindowType. } }; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LoomInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LoomInventoryTranslator.java new file mode 100644 index 00000000..5aa716bd --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LoomInventoryTranslator.java @@ -0,0 +1,85 @@ +/* + * 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.translators; + +import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; +import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; +import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData; +import org.geysermc.connector.network.translators.inventory.BedrockContainerSlot; +import org.geysermc.connector.network.translators.inventory.updater.UIInventoryUpdater; + +public class LoomInventoryTranslator extends AbstractBlockInventoryTranslator { + public LoomInventoryTranslator() { + super(4, "minecraft:loom[facing=north]", ContainerType.LOOM, UIInventoryUpdater.INSTANCE); + } + + @Override + public int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData) { + if (slotInfoData.getContainer() == ContainerSlotType.LOOM_INPUT) { + return 0; + } + if (slotInfoData.getContainer() == ContainerSlotType.LOOM_DYE) { + return 1; + } + if (slotInfoData.getContainer() == ContainerSlotType.LOOM_MATERIAL) { + return 2; + } + if (slotInfoData.getContainer() == ContainerSlotType.LOOM_RESULT || slotInfoData.getContainer() == ContainerSlotType.CREATIVE_OUTPUT) { + return 3; + } + return super.bedrockSlotToJava(slotInfoData); + } + + @Override + public BedrockContainerSlot javaSlotToBedrockContainer(int slot) { + switch (slot) { + case 0: + return new BedrockContainerSlot(ContainerSlotType.LOOM_INPUT, 9); + case 1: + return new BedrockContainerSlot(ContainerSlotType.LOOM_DYE, 10); + case 2: + return new BedrockContainerSlot(ContainerSlotType.LOOM_MATERIAL, 11); + case 3: + return new BedrockContainerSlot(ContainerSlotType.LOOM_RESULT, 50); + } + return super.javaSlotToBedrockContainer(slot); + } + + @Override + public int javaSlotToBedrock(int slot) { + switch (slot) { + case 0: + return 9; + case 1: + return 10; + case 2: + return 11; + case 3: + return 50; + } + return super.javaSlotToBedrock(slot); + } +} From 6ae81cce527e0446d09dbd78eca1b988f680a001 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 23 Dec 2020 22:53:54 -0500 Subject: [PATCH 14/39] Full banner loom support --- .../inventory/InventoryTranslator.java | 10 +- .../BeaconInventoryTranslator.java | 2 +- .../EnchantingInventoryTranslator.java | 2 +- .../translators/LoomInventoryTranslator.java | 92 +++++++++++++++++++ 4 files changed, 99 insertions(+), 7 deletions(-) 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 1dc8fa7d..bde448ab 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 @@ -85,6 +85,7 @@ public abstract class InventoryTranslator { put(WindowType.CRAFTING, new CraftingInventoryTranslator()); put(WindowType.ENCHANTMENT, new EnchantingInventoryTranslator()); put(WindowType.GRINDSTONE, new GrindstoneInventoryTranslator()); + put(WindowType.LOOM, new LoomInventoryTranslator()); put(WindowType.MERCHANT, new MerchantInventoryTranslator()); put(WindowType.SHULKER_BOX, new ShulkerInventoryTranslator()); put(WindowType.SMITHING, new SmithingInventoryTranslator()); @@ -93,10 +94,9 @@ public abstract class InventoryTranslator { put(WindowType.GENERIC_3X3, new GenericBlockInventoryTranslator(9, "minecraft:dispenser[facing=north,triggered=false]", ContainerType.DISPENSER)); put(WindowType.HOPPER, new GenericBlockInventoryTranslator(5, "minecraft:hopper[enabled=false,facing=down]", ContainerType.HOPPER)); - /* Workstations */ + /* todo */ //put(WindowType.CARTOGRAPHY //put(WindowType.STONECUTTER - put(WindowType.LOOM, new LoomInventoryTranslator()); //put(WindowType. } }; @@ -122,12 +122,12 @@ public abstract class InventoryTranslator { * Should be overrided if this request matches a certain criteria and shouldn't be treated normally. * E.G. anvil renaming or enchanting */ - public boolean shouldHandleRequestFirst(StackRequestActionData action) { + public boolean shouldHandleRequestFirst(StackRequestActionData action, Inventory inventory) { return false; } /** - * If {@link #shouldHandleRequestFirst(StackRequestActionData)} returns true, this will be called + * If {@link #shouldHandleRequestFirst(StackRequestActionData, Inventory)} returns true, this will be called */ public ItemStackResponsePacket.Response translateSpecialRequest(GeyserSession session, Inventory inventory, ItemStackRequestPacket.Request request) { return null; @@ -138,7 +138,7 @@ public abstract class InventoryTranslator { for (ItemStackRequestPacket.Request request : requests) { if (request.getActions().length > 0) { StackRequestActionData firstAction = request.getActions()[0]; - if (shouldHandleRequestFirst(firstAction)) { + if (shouldHandleRequestFirst(firstAction, inventory)) { // Some special request that shouldn't be processed normally responsePacket.getEntries().add(translateSpecialRequest(session, inventory, request)); } else if (firstAction.getType() == StackRequestActionType.CRAFT_RECIPE || firstAction.getType() == StackRequestActionType.CRAFT_RECIPE_AUTO) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/BeaconInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/BeaconInventoryTranslator.java index 678d7e84..8ffc3e1a 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/BeaconInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/BeaconInventoryTranslator.java @@ -89,7 +89,7 @@ public class BeaconInventoryTranslator extends AbstractBlockInventoryTranslator } @Override - public boolean shouldHandleRequestFirst(StackRequestActionData action) { + public boolean shouldHandleRequestFirst(StackRequestActionData action, Inventory inventory) { return action.getType() == StackRequestActionType.BEACON_PAYMENT; } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/EnchantingInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/EnchantingInventoryTranslator.java index cfa02419..ea836e3b 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/EnchantingInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/EnchantingInventoryTranslator.java @@ -105,7 +105,7 @@ public class EnchantingInventoryTranslator extends AbstractBlockInventoryTransla } @Override - public boolean shouldHandleRequestFirst(StackRequestActionData action) { + public boolean shouldHandleRequestFirst(StackRequestActionData action, Inventory inventory) { return action.getType() == StackRequestActionType.CRAFT_RECIPE; } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LoomInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LoomInventoryTranslator.java index 5aa716bd..db5fc963 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LoomInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LoomInventoryTranslator.java @@ -25,17 +25,109 @@ package org.geysermc.connector.network.translators.inventory.translators; +import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientClickWindowButtonPacket; +import com.nukkitx.nbt.NbtMap; +import com.nukkitx.nbt.NbtType; import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData; +import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.CraftResultsDeprecatedStackRequestActionData; +import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.StackRequestActionData; +import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.StackRequestActionType; +import com.nukkitx.protocol.bedrock.packet.ItemStackRequestPacket; +import com.nukkitx.protocol.bedrock.packet.ItemStackResponsePacket; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import org.geysermc.connector.inventory.Inventory; +import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.inventory.BedrockContainerSlot; import org.geysermc.connector.network.translators.inventory.updater.UIInventoryUpdater; +import java.util.List; + public class LoomInventoryTranslator extends AbstractBlockInventoryTranslator { + /** + * A map of Bedrock patterns to Java index. Used to request for a specific banner pattern. + */ + private static final Object2IntMap PATTERN_TO_INDEX = new Object2IntOpenHashMap<>(); + + static { + // Added from left-to-right then up-to-down in the order Java presents it + int index = 1; + PATTERN_TO_INDEX.put("bl", index++); + PATTERN_TO_INDEX.put("br", index++); + PATTERN_TO_INDEX.put("tl", index++); + PATTERN_TO_INDEX.put("tr", index++); + PATTERN_TO_INDEX.put("bs", index++); + PATTERN_TO_INDEX.put("ts", index++); + PATTERN_TO_INDEX.put("ls", index++); + PATTERN_TO_INDEX.put("rs", index++); + PATTERN_TO_INDEX.put("cs", index++); + PATTERN_TO_INDEX.put("ms", index++); + PATTERN_TO_INDEX.put("drs", index++); + PATTERN_TO_INDEX.put("dls", index++); + PATTERN_TO_INDEX.put("ss", index++); + PATTERN_TO_INDEX.put("cr", index++); + PATTERN_TO_INDEX.put("sc", index++); + PATTERN_TO_INDEX.put("bt", index++); + PATTERN_TO_INDEX.put("tt", index++); + PATTERN_TO_INDEX.put("bts", index++); + PATTERN_TO_INDEX.put("tts", index++); + PATTERN_TO_INDEX.put("ld", index++); + PATTERN_TO_INDEX.put("rd", index++); + PATTERN_TO_INDEX.put("lud", index++); + PATTERN_TO_INDEX.put("rud", index++); + PATTERN_TO_INDEX.put("mc", index++); + PATTERN_TO_INDEX.put("mr", index++); + PATTERN_TO_INDEX.put("vh", index++); + PATTERN_TO_INDEX.put("hh", index++); + PATTERN_TO_INDEX.put("vhr", index++); + PATTERN_TO_INDEX.put("hhb", index++); + PATTERN_TO_INDEX.put("bo", index++); + index++; // Bordure indented, does not appear to exist in Bedrock? + PATTERN_TO_INDEX.put("gra", index++); + PATTERN_TO_INDEX.put("gru", index); + // Bricks do not appear to be a pattern on Bedrock, either + } + public LoomInventoryTranslator() { super(4, "minecraft:loom[facing=north]", ContainerType.LOOM, UIInventoryUpdater.INSTANCE); } + @Override + public boolean shouldHandleRequestFirst(StackRequestActionData action, Inventory inventory) { + // If the LOOM_MATERIAL slot is not empty, we are crafting a pattern that does not come from an item + return action.getType() == StackRequestActionType.CRAFT_NON_IMPLEMENTED_DEPRECATED && inventory.getItem(2).isEmpty(); + } + + @Override + public ItemStackResponsePacket.Response translateSpecialRequest(GeyserSession session, Inventory inventory, ItemStackRequestPacket.Request request) { + // TODO: I anticipate this will be changed in the future to use something non-deprecated. Keep an eye out. + // Also TODO: Shift-clicking doesn't work here. + StackRequestActionData data = request.getActions()[1]; + if (!(data instanceof CraftResultsDeprecatedStackRequestActionData)) { + return rejectRequest(request); + } + CraftResultsDeprecatedStackRequestActionData craftData = (CraftResultsDeprecatedStackRequestActionData) data; + // Get the patterns compound tag + List blockEntityTag = craftData.getResultItems()[0].getTag().getList("Patterns", NbtType.COMPOUND); + // Get the pattern that the Bedrock client requests - the last pattern in the Patterns list + String pattern = blockEntityTag.get(blockEntityTag.size() - 1).getString("Pattern"); + // Get the Java index of this pattern + int index = PATTERN_TO_INDEX.getOrDefault(pattern, -1); + if (index == -1) { + return rejectRequest(request); + } + // Java's formula: 4 * row + col + // And the Java loom window has a fixed row/width of four + // So... Number / 4 = row (so we don't have to bother there), and number % 4 is our column, which leads us back to our index. :) + ClientClickWindowButtonPacket packet = new ClientClickWindowButtonPacket(inventory.getId(), index); + System.out.println(packet); + session.sendDownstreamPacket(packet); + + return translateRequest(session, inventory, request); + } + @Override public int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData) { if (slotInfoData.getContainer() == ContainerSlotType.LOOM_INPUT) { From 617a1216d5f6226b23344a36f617d433462cd24c Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Thu, 24 Dec 2020 11:23:47 -0500 Subject: [PATCH 15/39] Initial work on stonecutters --- .../network/session/GeyserSession.java | 19 +-- .../inventory/InventoryTranslator.java | 4 +- .../StonecutterInventoryTranslator.java | 118 ++++++++++++++++++ .../java/JavaDeclareRecipesTranslator.java | 23 ++++ 4 files changed, 153 insertions(+), 11 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/StonecutterInventoryTranslator.java diff --git a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java index 9f29acbb..5d0e2604 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java @@ -32,8 +32,8 @@ import com.github.steveice10.mc.protocol.MinecraftConstants; import com.github.steveice10.mc.protocol.MinecraftProtocol; import com.github.steveice10.mc.protocol.data.SubProtocol; import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; -import com.github.steveice10.mc.protocol.data.game.statistic.Statistic; import com.github.steveice10.mc.protocol.data.game.recipe.Recipe; +import com.github.steveice10.mc.protocol.data.game.statistic.Statistic; import com.github.steveice10.mc.protocol.data.game.window.VillagerTrade; import com.github.steveice10.mc.protocol.packet.handshake.client.HandshakePacket; import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPositionRotationPacket; @@ -53,6 +53,7 @@ import com.nukkitx.protocol.bedrock.data.command.CommandPermission; import com.nukkitx.protocol.bedrock.packet.*; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.ints.IntSet; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; @@ -93,15 +94,8 @@ import java.net.InetSocketAddress; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.spec.InvalidKeySpecException; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; -import java.util.concurrent.TimeUnit; import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; @Getter @@ -218,6 +212,13 @@ public class GeyserSession implements CommandSender { @Setter private Int2ObjectMap craftingRecipes; + /** + * Saves a list of all stonecutter recipes, for use in a stonecutter inventory. + * The key is the Java ID of the item; the values are all the possible outputs' Java IDs + */ + @Setter + private Int2ObjectMap stonecutterRecipes; + /** * The current attack speed of the player. Used for sending proper cooldown timings. * Setting a default fixes cooldowns not showing up on a fresh world. 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 bde448ab..d2fb12a0 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 @@ -89,6 +89,7 @@ public abstract class InventoryTranslator { put(WindowType.MERCHANT, new MerchantInventoryTranslator()); put(WindowType.SHULKER_BOX, new ShulkerInventoryTranslator()); put(WindowType.SMITHING, new SmithingInventoryTranslator()); + put(WindowType.STONECUTTER, new StonecutterInventoryTranslator()); /* Generics */ put(WindowType.GENERIC_3X3, new GenericBlockInventoryTranslator(9, "minecraft:dispenser[facing=north,triggered=false]", ContainerType.DISPENSER)); @@ -96,8 +97,7 @@ public abstract class InventoryTranslator { /* todo */ //put(WindowType.CARTOGRAPHY - //put(WindowType.STONECUTTER - //put(WindowType. + // horse } }; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/StonecutterInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/StonecutterInventoryTranslator.java new file mode 100644 index 00000000..dd6c3b36 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/StonecutterInventoryTranslator.java @@ -0,0 +1,118 @@ +/* + * 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.translators; + +import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; +import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientClickWindowButtonPacket; +import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; +import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; +import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData; +import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.CraftResultsDeprecatedStackRequestActionData; +import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.StackRequestActionData; +import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.StackRequestActionType; +import com.nukkitx.protocol.bedrock.packet.ItemStackRequestPacket; +import com.nukkitx.protocol.bedrock.packet.ItemStackResponsePacket; +import it.unimi.dsi.fastutil.ints.IntSet; +import org.geysermc.connector.inventory.GeyserItemStack; +import org.geysermc.connector.inventory.Inventory; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.inventory.BedrockContainerSlot; +import org.geysermc.connector.network.translators.inventory.updater.UIInventoryUpdater; +import org.geysermc.connector.network.translators.item.ItemTranslator; + +import java.util.stream.Collectors; + +public class StonecutterInventoryTranslator extends AbstractBlockInventoryTranslator { + public StonecutterInventoryTranslator() { + super(2, "minecraft:stonecutter[facing=north]", ContainerType.STONECUTTER, UIInventoryUpdater.INSTANCE); + } + + @Override + public boolean shouldHandleRequestFirst(StackRequestActionData action, Inventory inventory) { + return action.getType() == StackRequestActionType.CRAFT_NON_IMPLEMENTED_DEPRECATED; + } + + @Override + public ItemStackResponsePacket.Response translateSpecialRequest(GeyserSession session, Inventory inventory, ItemStackRequestPacket.Request request) { + // TODO: Also surely to change in the future + StackRequestActionData data = request.getActions()[1]; + if (!(data instanceof CraftResultsDeprecatedStackRequestActionData)) { + return rejectRequest(request); + } + CraftResultsDeprecatedStackRequestActionData craftData = (CraftResultsDeprecatedStackRequestActionData) data; + // Get the ID of the item we are cutting + int id = inventory.getItem(0).getId(); + // Look up all possible options of cutting from this ID + IntSet results = session.getStonecutterRecipes().get(id); + if (results == null) { + return rejectRequest(request); + } + ItemStack javaOutput = ItemTranslator.translateToJava(craftData.getResultItems()[0]); + // TODO bruh + // Getting the index of the item in the Java stonecutter list + // We do need to sort them by their Java ID to preserve index, though + int index = results.stream().sorted().collect(Collectors.toList()).indexOf(javaOutput.getId()); + ClientClickWindowButtonPacket packet = new ClientClickWindowButtonPacket(inventory.getId(), index + 1); + System.out.println(packet.toString()); + session.sendDownstreamPacket(packet); + // We don't know there is an output here, so we tell ourselves that there is + inventory.setItem(1, GeyserItemStack.from(javaOutput, session.getItemNetId().incrementAndGet())); + return translateRequest(session, inventory, request); + } + + @Override + public int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData) { + if (slotInfoData.getContainer() == ContainerSlotType.STONECUTTER_INPUT) { + return 0; + } + if (slotInfoData.getContainer() == ContainerSlotType.STONECUTTER_RESULT || slotInfoData.getContainer() == ContainerSlotType.CREATIVE_OUTPUT) { + return 1; + } + return super.bedrockSlotToJava(slotInfoData); + } + + @Override + public BedrockContainerSlot javaSlotToBedrockContainer(int slot) { + if (slot == 0) { + return new BedrockContainerSlot(ContainerSlotType.STONECUTTER_INPUT, 3); + } + if (slot == 1) { + return new BedrockContainerSlot(ContainerSlotType.STONECUTTER_RESULT, 50); + } + return super.javaSlotToBedrockContainer(slot); + } + + @Override + public int javaSlotToBedrock(int slot) { + if (slot == 0) { + return 3; + } + if (slot == 1) { + return 50; + } + return super.javaSlotToBedrock(slot); + } +} 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 824b5acd..c475396c 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 @@ -25,10 +25,12 @@ package org.geysermc.connector.network.translators.java; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; import com.github.steveice10.mc.protocol.data.game.recipe.Ingredient; import com.github.steveice10.mc.protocol.data.game.recipe.Recipe; import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapedRecipeData; import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapelessRecipeData; +import com.github.steveice10.mc.protocol.data.game.recipe.data.StoneCuttingRecipeData; import com.github.steveice10.mc.protocol.packet.ingame.server.ServerDeclareRecipesPacket; import com.nukkitx.nbt.NbtMap; import com.nukkitx.protocol.bedrock.data.inventory.CraftingData; @@ -56,6 +58,7 @@ public class JavaDeclareRecipesTranslator extends PacketTranslator recipeMap = new Int2ObjectOpenHashMap<>(); + Int2ObjectMap stonecutterRecipeMap = new Int2ObjectOpenHashMap<>(); CraftingDataPacket craftingDataPacket = new CraftingDataPacket(); craftingDataPacket.setCleanRecipes(true); for (Recipe recipe : packet.getRecipes()) { @@ -136,11 +139,31 @@ public class JavaDeclareRecipesTranslator extends PacketTranslator Date: Thu, 24 Dec 2020 18:29:25 -0500 Subject: [PATCH 16/39] Stonecutter fixed; Loom improved --- .../network/session/GeyserSession.java | 6 +- .../inventory/InventoryTranslator.java | 24 ++++++-- .../EnchantingInventoryTranslator.java | 2 +- .../translators/LoomInventoryTranslator.java | 51 +++++++++++++++-- .../StonecutterInventoryTranslator.java | 28 ++++++---- .../item/translators/BannerTranslator.java | 5 +- .../java/JavaDeclareRecipesTranslator.java | 55 ++++++++++++------- 7 files changed, 129 insertions(+), 42 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java index 5d0e2604..880e8712 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java @@ -53,7 +53,7 @@ import com.nukkitx.protocol.bedrock.data.command.CommandPermission; import com.nukkitx.protocol.bedrock.packet.*; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -import it.unimi.dsi.fastutil.ints.IntSet; +import it.unimi.dsi.fastutil.ints.IntList; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; @@ -214,10 +214,10 @@ public class GeyserSession implements CommandSender { /** * Saves a list of all stonecutter recipes, for use in a stonecutter inventory. - * The key is the Java ID of the item; the values are all the possible outputs' Java IDs + * The key is the Java ID of the item; the values are all the possible outputs' Java IDs sorted by their string identifier */ @Setter - private Int2ObjectMap stonecutterRecipes; + private Int2ObjectMap stonecutterRecipes; /** * The current attack speed of the player. Used for sending proper cooldown timings. 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 d2fb12a0..b3f2475d 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 @@ -118,6 +118,16 @@ public abstract class InventoryTranslator { public abstract SlotType getSlotType(int javaSlot); public abstract Inventory createInventory(String name, int windowId, WindowType windowType, PlayerInventory playerInventory); + /** + * Should be overwritten in cases where specific inventories should reject an item being in a specific spot. + * For examples, looms use this to reject items that are dyes in Bedrock but not in Java. + * + * @return true if this transfer should be rejected + */ + public boolean shouldRejectItemPlace(GeyserSession session, Inventory inventory, int javaDestinationSlot) { + return false; + } + /** * Should be overrided if this request matches a certain criteria and shouldn't be treated normally. * E.G. anvil renaming or enchanting @@ -178,12 +188,18 @@ public abstract class InventoryTranslator { return rejectRequest(request); } + int destSlot = bedrockSlotToJava(transferAction.getDestination()); + + if (shouldRejectItemPlace(session, inventory, destSlot)) { + // This item would not be here in Java + return rejectRequest(request, false); + } + if (isCursor(transferAction.getSource()) && isCursor(transferAction.getDestination())) { //??? return rejectRequest(request); } else if (session.getGameMode().equals(GameMode.CREATIVE) && inventory instanceof PlayerInventory) { // TODO: does the Java server use this stuff all the time in creative? // Creative acts a little differently because it just edits slots int sourceSlot = bedrockSlotToJava(transferAction.getSource()); - int destSlot = bedrockSlotToJava(transferAction.getDestination()); boolean sourceIsCursor = isCursor(transferAction.getSource()); boolean destIsCursor = isCursor(transferAction.getDestination()); @@ -249,7 +265,6 @@ public abstract class InventoryTranslator { } else if (isCursor(transferAction.getSource())) { //releasing cursor int sourceAmount = cursor.getAmount(); - int destSlot = bedrockSlotToJava(transferAction.getDestination()); if (transferAction.getCount() == sourceAmount) { //release all plan.add(Click.LEFT, destSlot); } else { //release some @@ -279,7 +294,9 @@ public abstract class InventoryTranslator { if (transferAction.getCount() != sourceAmount) { //TODO: handle partially picking up into non-empty cursor (temp slot) return rejectRequest(request); } - plan.add(Click.LEFT, sourceSlot); //release cursor onto source slot + if (getSlotType(sourceSlot).equals(SlotType.NORMAL)) { + plan.add(Click.LEFT, sourceSlot); //release cursor onto source slot + } plan.add(Click.LEFT, sourceSlot); //pickup combined cursor and source } } else { //transfer from one slot to another @@ -288,7 +305,6 @@ public abstract class InventoryTranslator { } int sourceSlot = bedrockSlotToJava(transferAction.getSource()); int sourceAmount = plan.getItem(sourceSlot).getAmount(); - int destSlot = bedrockSlotToJava(transferAction.getDestination()); if (transferAction.getCount() == sourceAmount) { //transfer all plan.add(Click.LEFT, sourceSlot); //pickup source plan.add(Click.LEFT, destSlot); //let go of all items and done diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/EnchantingInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/EnchantingInventoryTranslator.java index ea836e3b..c8ae8e2d 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/EnchantingInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/EnchantingInventoryTranslator.java @@ -127,7 +127,7 @@ public class EnchantingInventoryTranslator extends AbstractBlockInventoryTransla } if (javaSlot == -1) { // Slot should be determined as 0, 1, or 2 - throw new RuntimeException("Cannot find enchant slot for item!"); + return rejectRequest(request); } ClientClickWindowButtonPacket packet = new ClientClickWindowButtonPacket(inventory.getId(), javaSlot); System.out.println(packet); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LoomInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LoomInventoryTranslator.java index db5fc963..bacc83a0 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LoomInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LoomInventoryTranslator.java @@ -26,6 +26,8 @@ package org.geysermc.connector.network.translators.inventory.translators; import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientClickWindowButtonPacket; +import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import com.github.steveice10.opennbt.tag.builtin.ListTag; import com.nukkitx.nbt.NbtMap; import com.nukkitx.nbt.NbtType; import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; @@ -38,11 +40,15 @@ import com.nukkitx.protocol.bedrock.packet.ItemStackRequestPacket; import com.nukkitx.protocol.bedrock.packet.ItemStackResponsePacket; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import org.geysermc.connector.inventory.GeyserItemStack; import org.geysermc.connector.inventory.Inventory; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.inventory.BedrockContainerSlot; import org.geysermc.connector.network.translators.inventory.updater.UIInventoryUpdater; +import org.geysermc.connector.network.translators.item.ItemRegistry; +import org.geysermc.connector.network.translators.item.translators.BannerTranslator; +import java.util.Collections; import java.util.List; public class LoomInventoryTranslator extends AbstractBlockInventoryTranslator { @@ -94,6 +100,20 @@ public class LoomInventoryTranslator extends AbstractBlockInventoryTranslator { super(4, "minecraft:loom[facing=north]", ContainerType.LOOM, UIInventoryUpdater.INSTANCE); } + @Override + public boolean shouldRejectItemPlace(GeyserSession session, Inventory inventory, int javaDestinationSlot) { + if (javaDestinationSlot != 1) { + return false; + } + GeyserItemStack itemStack = session.getPlayerInventory().getCursor(); + if (itemStack.isEmpty()) { + return false; + } + + // Reject the item if Bedrock is attempting to put in a dye that is not a dye in Java Edition + return !ItemRegistry.getItem(itemStack.getItemStack()).getJavaIdentifier().contains("_dye"); + } + @Override public boolean shouldHandleRequestFirst(StackRequestActionData action, Inventory inventory) { // If the LOOM_MATERIAL slot is not empty, we are crafting a pattern that does not come from an item @@ -103,18 +123,17 @@ public class LoomInventoryTranslator extends AbstractBlockInventoryTranslator { @Override public ItemStackResponsePacket.Response translateSpecialRequest(GeyserSession session, Inventory inventory, ItemStackRequestPacket.Request request) { // TODO: I anticipate this will be changed in the future to use something non-deprecated. Keep an eye out. - // Also TODO: Shift-clicking doesn't work here. StackRequestActionData data = request.getActions()[1]; if (!(data instanceof CraftResultsDeprecatedStackRequestActionData)) { return rejectRequest(request); } CraftResultsDeprecatedStackRequestActionData craftData = (CraftResultsDeprecatedStackRequestActionData) data; // Get the patterns compound tag - List blockEntityTag = craftData.getResultItems()[0].getTag().getList("Patterns", NbtType.COMPOUND); + List newblockEntityTag = craftData.getResultItems()[0].getTag().getList("Patterns", NbtType.COMPOUND); // Get the pattern that the Bedrock client requests - the last pattern in the Patterns list - String pattern = blockEntityTag.get(blockEntityTag.size() - 1).getString("Pattern"); + NbtMap pattern = newblockEntityTag.get(newblockEntityTag.size() - 1); // Get the Java index of this pattern - int index = PATTERN_TO_INDEX.getOrDefault(pattern, -1); + int index = PATTERN_TO_INDEX.getOrDefault(pattern.getString("Pattern"), -1); if (index == -1) { return rejectRequest(request); } @@ -124,6 +143,30 @@ public class LoomInventoryTranslator extends AbstractBlockInventoryTranslator { ClientClickWindowButtonPacket packet = new ClientClickWindowButtonPacket(inventory.getId(), index); System.out.println(packet); session.sendDownstreamPacket(packet); + GeyserItemStack inputCopy = inventory.getItem(0).copy(); + inputCopy.setNetId(session.getItemNetId().incrementAndGet()); + // Add the pattern manually, for better item synchronization + if (inputCopy.getNbt() == null) { + inputCopy.setNbt(new CompoundTag("")); + } + CompoundTag blockEntityTag = inputCopy.getNbt().get("BlockEntityTag"); + CompoundTag javaBannerPattern = BannerTranslator.getJavaBannerPattern(pattern); + if (blockEntityTag != null) { + ListTag patternsList = blockEntityTag.get("Patterns"); + if (patternsList != null) { + patternsList.add(javaBannerPattern); + } else { + patternsList = new ListTag("Patterns", Collections.singletonList(javaBannerPattern)); + blockEntityTag.put(patternsList); + } + } else { + blockEntityTag = new CompoundTag("BlockEntityTag"); + ListTag patternsList = new ListTag("Patterns", Collections.singletonList(javaBannerPattern)); + blockEntityTag.put(patternsList); + inputCopy.getNbt().put(blockEntityTag); + } + // Set the new item as the output + inventory.setItem(3, inputCopy); return translateRequest(session, inventory, request); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/StonecutterInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/StonecutterInventoryTranslator.java index dd6c3b36..7bac3c65 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/StonecutterInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/StonecutterInventoryTranslator.java @@ -35,16 +35,15 @@ import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.StackRequ import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.StackRequestActionType; import com.nukkitx.protocol.bedrock.packet.ItemStackRequestPacket; import com.nukkitx.protocol.bedrock.packet.ItemStackResponsePacket; -import it.unimi.dsi.fastutil.ints.IntSet; +import it.unimi.dsi.fastutil.ints.IntList; import org.geysermc.connector.inventory.GeyserItemStack; import org.geysermc.connector.inventory.Inventory; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.inventory.BedrockContainerSlot; +import org.geysermc.connector.network.translators.inventory.SlotType; import org.geysermc.connector.network.translators.inventory.updater.UIInventoryUpdater; import org.geysermc.connector.network.translators.item.ItemTranslator; -import java.util.stream.Collectors; - public class StonecutterInventoryTranslator extends AbstractBlockInventoryTranslator { public StonecutterInventoryTranslator() { super(2, "minecraft:stonecutter[facing=north]", ContainerType.STONECUTTER, UIInventoryUpdater.INSTANCE); @@ -66,20 +65,21 @@ public class StonecutterInventoryTranslator extends AbstractBlockInventoryTransl // Get the ID of the item we are cutting int id = inventory.getItem(0).getId(); // Look up all possible options of cutting from this ID - IntSet results = session.getStonecutterRecipes().get(id); + IntList results = session.getStonecutterRecipes().get(id); if (results == null) { return rejectRequest(request); } + System.out.println(id + " " + results); ItemStack javaOutput = ItemTranslator.translateToJava(craftData.getResultItems()[0]); - // TODO bruh + System.out.println(javaOutput); // Getting the index of the item in the Java stonecutter list - // We do need to sort them by their Java ID to preserve index, though - int index = results.stream().sorted().collect(Collectors.toList()).indexOf(javaOutput.getId()); - ClientClickWindowButtonPacket packet = new ClientClickWindowButtonPacket(inventory.getId(), index + 1); + ClientClickWindowButtonPacket packet = new ClientClickWindowButtonPacket(inventory.getId(), results.indexOf(javaOutput.getId())); System.out.println(packet.toString()); session.sendDownstreamPacket(packet); - // We don't know there is an output here, so we tell ourselves that there is - inventory.setItem(1, GeyserItemStack.from(javaOutput, session.getItemNetId().incrementAndGet())); + if (inventory.getItem(1).getId() != javaOutput.getId()) { + // We don't know there is an output here, so we tell ourselves that there is + inventory.setItem(1, GeyserItemStack.from(javaOutput, session.getItemNetId().incrementAndGet())); + } return translateRequest(session, inventory, request); } @@ -115,4 +115,12 @@ public class StonecutterInventoryTranslator extends AbstractBlockInventoryTransl } return super.javaSlotToBedrock(slot); } + + @Override + public SlotType getSlotType(int javaSlot) { + if (javaSlot == 1) { + return SlotType.OUTPUT; + } + return super.getSlotType(javaSlot); + } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/BannerTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/BannerTranslator.java index 5e5bc354..1870a127 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/BannerTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/BannerTranslator.java @@ -32,6 +32,7 @@ import com.nukkitx.nbt.NbtMap; import com.nukkitx.nbt.NbtMapBuilder; import com.nukkitx.nbt.NbtType; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; +import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.network.translators.ItemRemapper; import org.geysermc.connector.network.translators.item.ItemEntry; import org.geysermc.connector.network.translators.item.ItemRegistry; @@ -181,6 +182,7 @@ public class BannerTranslator extends ItemTranslator { @Override public ItemStack translateToJava(ItemData itemData, ItemEntry itemEntry) { + GeyserConnector.getInstance().getLogger().warning(itemEntry.toString()); if (itemData.getTag() == null) { return super.translateToJava(itemData, itemEntry); } @@ -195,13 +197,14 @@ public class BannerTranslator extends ItemTranslator { blockEntityTag.put(OMINOUS_BANNER_PATTERN); itemStack.getNbt().put(blockEntityTag); - } else if (nbtTag.containsKey("Patterns", NbtType.COMPOUND)) { + } else if (nbtTag.containsKey("Patterns", NbtType.LIST)) { List patterns = nbtTag.getList("Patterns", NbtType.COMPOUND); CompoundTag blockEntityTag = new CompoundTag("BlockEntityTag"); blockEntityTag.put(convertBannerPattern(patterns)); itemStack.getNbt().put(blockEntityTag); + itemStack.getNbt().remove("Patterns"); // Remove the old Bedrock patterns list } return itemStack; 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 c475396c..e74a8232 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 @@ -36,10 +36,7 @@ import com.nukkitx.nbt.NbtMap; import com.nukkitx.protocol.bedrock.data.inventory.CraftingData; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import com.nukkitx.protocol.bedrock.packet.CraftingDataPacket; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -import it.unimi.dsi.fastutil.ints.IntOpenHashSet; -import it.unimi.dsi.fastutil.ints.IntSet; +import it.unimi.dsi.fastutil.ints.*; import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import org.geysermc.connector.network.session.GeyserSession; @@ -58,7 +55,7 @@ public class JavaDeclareRecipesTranslator extends PacketTranslator recipeMap = new Int2ObjectOpenHashMap<>(); - Int2ObjectMap stonecutterRecipeMap = new Int2ObjectOpenHashMap<>(); + Int2ObjectMap> unsortedStonecutterData = new Int2ObjectOpenHashMap<>(); CraftingDataPacket craftingDataPacket = new CraftingDataPacket(); craftingDataPacket.setCleanRecipes(true); for (Recipe recipe : packet.getRecipes()) { @@ -141,26 +138,46 @@ public class JavaDeclareRecipesTranslator extends PacketTranslator data = unsortedStonecutterData.get(ingredient.getId()); + if (data == null) { + data = new ArrayList<>(); + unsortedStonecutterData.put(ingredient.getId(), data); } - // Add the ingredient as the key and all possible values as the value - outputs.add(stoneCuttingData.getResult().getId()); + data.add(stoneCuttingData); + // Save for processing after all recipes have been received } } } craftingDataPacket.getPotionMixData().addAll(PotionMixRegistry.POTION_MIXES); + + Int2ObjectMap stonecutterRecipeMap = new Int2ObjectOpenHashMap<>(); + for (Int2ObjectMap.Entry> data : unsortedStonecutterData.int2ObjectEntrySet()) { + data.getValue().sort(Comparator.comparing((stoneCuttingRecipeData -> + // Sort the list by each output item's Java identifier - this is how it's sorted on Java, and therefore + // We can get the correct order for button pressing + ItemRegistry.getItem(stoneCuttingRecipeData.getResult()).getJavaIdentifier()))); + // Now that it's sorted, let's translate these recipes + for (StoneCuttingRecipeData stoneCuttingData : data.getValue()) { + // As of 1.16.4, all stonecutter recipes have one ingredient option + ItemStack ingredient = stoneCuttingData.getIngredient().getOptions()[0]; + ItemData input = ItemTranslator.translateToBedrock(session, ingredient); + ItemData output = ItemTranslator.translateToBedrock(session, stoneCuttingData.getResult()); + UUID uuid = UUID.randomUUID(); + // We need to register stonecutting recipes so they show up on Bedrock + craftingDataPacket.getCraftingData().add(CraftingData.fromShapeless(uuid.toString(), + Collections.singletonList(input), Collections.singletonList(output), uuid, "stonecutter", 0, netId++)); + // Save the recipe list for reference when crafting + IntList outputs = stonecutterRecipeMap.get(ingredient.getId()); + if (outputs == null) { + outputs = new IntArrayList(); + // Add the ingredient as the key and all possible values as the value + stonecutterRecipeMap.put(ingredient.getId(), outputs); + } + outputs.add(stoneCuttingData.getResult().getId()); + } + } + session.sendUpstreamPacket(craftingDataPacket); session.setCraftingRecipes(recipeMap); session.setStonecutterRecipes(stonecutterRecipeMap); From c7fade295ea93d31752f38dcf879ad70ce8c23d5 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Thu, 24 Dec 2020 20:43:24 -0500 Subject: [PATCH 17/39] Add swap support for creative mode; start on cartography table --- .../inventory/InventoryTranslator.java | 53 +++++++++-- .../CartographyInventoryTranslator.java | 95 +++++++++++++++++++ .../translators/LoomInventoryTranslator.java | 8 +- 3 files changed, 145 insertions(+), 11 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/CartographyInventoryTranslator.java 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 b3f2475d..3e5bf0c6 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 @@ -82,6 +82,7 @@ public abstract class InventoryTranslator { put(WindowType.ANVIL, new AnvilInventoryTranslator()); put(WindowType.BEACON, new BeaconInventoryTranslator()); put(WindowType.BREWING_STAND, new BrewingInventoryTranslator()); + put(WindowType.CARTOGRAPHY, new CartographyInventoryTranslator()); put(WindowType.CRAFTING, new CraftingInventoryTranslator()); put(WindowType.ENCHANTMENT, new EnchantingInventoryTranslator()); put(WindowType.GRINDSTONE, new GrindstoneInventoryTranslator()); @@ -96,7 +97,6 @@ public abstract class InventoryTranslator { put(WindowType.HOPPER, new GenericBlockInventoryTranslator(5, "minecraft:hopper[enabled=false,facing=down]", ContainerType.HOPPER)); /* todo */ - //put(WindowType.CARTOGRAPHY // horse } }; @@ -122,9 +122,11 @@ public abstract class InventoryTranslator { * Should be overwritten in cases where specific inventories should reject an item being in a specific spot. * For examples, looms use this to reject items that are dyes in Bedrock but not in Java. * + * javaSourceSlot will be -1 if the cursor is the source + * * @return true if this transfer should be rejected */ - public boolean shouldRejectItemPlace(GeyserSession session, Inventory inventory, int javaDestinationSlot) { + public boolean shouldRejectItemPlace(GeyserSession session, Inventory inventory, int javaSourceSlot, int javaDestinationSlot) { return false; } @@ -188,9 +190,10 @@ public abstract class InventoryTranslator { return rejectRequest(request); } + int sourceSlot = bedrockSlotToJava(transferAction.getSource()); int destSlot = bedrockSlotToJava(transferAction.getDestination()); - if (shouldRejectItemPlace(session, inventory, destSlot)) { + if (shouldRejectItemPlace(session, inventory, isCursor(transferAction.getSource()) ? -1 :sourceSlot, destSlot)) { // This item would not be here in Java return rejectRequest(request, false); } @@ -199,7 +202,6 @@ public abstract class InventoryTranslator { return rejectRequest(request); } else if (session.getGameMode().equals(GameMode.CREATIVE) && inventory instanceof PlayerInventory) { // TODO: does the Java server use this stuff all the time in creative? // Creative acts a little differently because it just edits slots - int sourceSlot = bedrockSlotToJava(transferAction.getSource()); boolean sourceIsCursor = isCursor(transferAction.getSource()); boolean destIsCursor = isCursor(transferAction.getDestination()); @@ -228,6 +230,15 @@ public abstract class InventoryTranslator { affectedSlots.add(destSlot); break; } + } else { + // Delete the source since we're moving it + inventory.setItem(sourceSlot, GeyserItemStack.EMPTY); + ClientCreativeInventoryActionPacket creativeActionPacket = new ClientCreativeInventoryActionPacket( + sourceSlot, + new ItemStack(0) + ); + session.sendDownstreamPacket(creativeActionPacket); + affectedSlots.add(sourceSlot); } // Update the item count with however much the client took newItem.setAmount(transferAction.getCount()); @@ -273,7 +284,6 @@ public abstract class InventoryTranslator { } } } else if (isCursor(transferAction.getDestination())) { //picking up into cursor - int sourceSlot = bedrockSlotToJava(transferAction.getSource()); GeyserItemStack sourceItem = plan.getItem(sourceSlot); int sourceAmount = sourceItem.getAmount(); if (cursor.isEmpty()) { //picking up into empty cursor @@ -303,7 +313,6 @@ public abstract class InventoryTranslator { if (!cursor.isEmpty()) { //TODO: handle slot transfer when cursor is already in use (temp slot) return rejectRequest(request); } - int sourceSlot = bedrockSlotToJava(transferAction.getSource()); int sourceAmount = plan.getItem(sourceSlot).getAmount(); if (transferAction.getCount() == sourceAmount) { //transfer all plan.add(Click.LEFT, sourceSlot); //pickup source @@ -339,7 +348,37 @@ public abstract class InventoryTranslator { if (!(checkNetId(session, inventory, swapAction.getSource()) && checkNetId(session, inventory, swapAction.getDestination()))) return rejectRequest(request); - if (isCursor(swapAction.getSource()) && isCursor(swapAction.getDestination())) { //??? + if (session.getGameMode().equals(GameMode.CREATIVE) && inventory instanceof PlayerInventory) { + int destSlot = bedrockSlotToJava(swapAction.getDestination()); + GeyserItemStack oldSourceItem; + GeyserItemStack oldDestinationItem = inventory.getItem(destSlot); + if (isCursor(swapAction.getSource())) { + oldSourceItem = session.getPlayerInventory().getCursor(); + session.getPlayerInventory().setCursor(oldDestinationItem); + } else { + int sourceSlot = bedrockSlotToJava(swapAction.getSource()); + oldSourceItem = inventory.getItem(sourceSlot); + ClientCreativeInventoryActionPacket creativeActionPacket = new ClientCreativeInventoryActionPacket( + sourceSlot, + oldDestinationItem.isEmpty() ? new ItemStack(0) : oldDestinationItem.getItemStack() // isEmpty check... just in case + ); + System.out.println(creativeActionPacket); + session.sendDownstreamPacket(creativeActionPacket); + inventory.setItem(sourceSlot, oldDestinationItem); + } + if (isCursor(swapAction.getDestination())) { + session.getPlayerInventory().setCursor(oldSourceItem); + } else { + ClientCreativeInventoryActionPacket creativeActionPacket = new ClientCreativeInventoryActionPacket( + destSlot, + oldSourceItem.isEmpty() ? new ItemStack(0) : oldSourceItem.getItemStack() + ); + System.out.println(creativeActionPacket); + session.sendDownstreamPacket(creativeActionPacket); + inventory.setItem(destSlot, oldSourceItem); + } + + } else if (isCursor(swapAction.getSource()) && isCursor(swapAction.getDestination())) { //??? return rejectRequest(request); } else if (isCursor(swapAction.getSource())) { //swap cursor int destSlot = bedrockSlotToJava(swapAction.getDestination()); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/CartographyInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/CartographyInventoryTranslator.java new file mode 100644 index 00000000..1ce6d078 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/CartographyInventoryTranslator.java @@ -0,0 +1,95 @@ +/* + * 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.translators; + +import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; +import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; +import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData; +import org.geysermc.connector.inventory.GeyserItemStack; +import org.geysermc.connector.inventory.Inventory; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.inventory.BedrockContainerSlot; +import org.geysermc.connector.network.translators.inventory.updater.UIInventoryUpdater; +import org.geysermc.connector.network.translators.item.ItemRegistry; + +public class CartographyInventoryTranslator extends AbstractBlockInventoryTranslator { + public CartographyInventoryTranslator() { + super(3, "minecraft:cartography_table", ContainerType.CARTOGRAPHY, UIInventoryUpdater.INSTANCE); + } + + @Override + public boolean shouldRejectItemPlace(GeyserSession session, Inventory inventory, int javaSourceSlot, int javaDestinationSlot) { + if (javaDestinationSlot == 1) { + // Bedrock Edition can use a compass to create locator maps in the ADDITIONAL slot + GeyserItemStack itemStack = javaSourceSlot == -1 ? session.getPlayerInventory().getCursor() : inventory.getItem(javaSourceSlot); + return ItemRegistry.getItem(itemStack.getItemStack()).getJavaIdentifier().equals("minecraft:compass"); + } else if (javaSourceSlot == 2) { + // Java doesn't allow an item to be renamed; this is why CARTOGRAPHY_ADDITIONAL could remain empty for Bedrock + return inventory.getItem(1).isEmpty(); + } + return false; + } + + @Override + public int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData) { + if (slotInfoData.getContainer() == ContainerSlotType.CARTOGRAPHY_INPUT) { + return 0; + } + if (slotInfoData.getContainer() == ContainerSlotType.CARTOGRAPHY_ADDITIONAL) { + return 1; + } + if (slotInfoData.getContainer() == ContainerSlotType.CARTOGRAPHY_RESULT || slotInfoData.getContainer() == ContainerSlotType.CREATIVE_OUTPUT) { + return 2; + } + return super.bedrockSlotToJava(slotInfoData); + } + + @Override + public BedrockContainerSlot javaSlotToBedrockContainer(int slot) { + switch (slot) { + case 0: + return new BedrockContainerSlot(ContainerSlotType.CARTOGRAPHY_INPUT, 12); + case 1: + return new BedrockContainerSlot(ContainerSlotType.CARTOGRAPHY_ADDITIONAL, 13); + case 2: + return new BedrockContainerSlot(ContainerSlotType.CARTOGRAPHY_RESULT, 50); + } + return super.javaSlotToBedrockContainer(slot); + } + + @Override + public int javaSlotToBedrock(int slot) { + switch (slot) { + case 0: + return 12; + case 1: + return 13; + case 2: + return 50; + } + return super.javaSlotToBedrock(slot); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LoomInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LoomInventoryTranslator.java index bacc83a0..47f21379 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LoomInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LoomInventoryTranslator.java @@ -101,11 +101,11 @@ public class LoomInventoryTranslator extends AbstractBlockInventoryTranslator { } @Override - public boolean shouldRejectItemPlace(GeyserSession session, Inventory inventory, int javaDestinationSlot) { + public boolean shouldRejectItemPlace(GeyserSession session, Inventory inventory, int javaSourceSlot, int javaDestinationSlot) { if (javaDestinationSlot != 1) { return false; } - GeyserItemStack itemStack = session.getPlayerInventory().getCursor(); + GeyserItemStack itemStack = javaSourceSlot == -1 ? session.getPlayerInventory().getCursor() : inventory.getItem(javaSourceSlot); if (itemStack.isEmpty()) { return false; } @@ -129,9 +129,9 @@ public class LoomInventoryTranslator extends AbstractBlockInventoryTranslator { } CraftResultsDeprecatedStackRequestActionData craftData = (CraftResultsDeprecatedStackRequestActionData) data; // Get the patterns compound tag - List newblockEntityTag = craftData.getResultItems()[0].getTag().getList("Patterns", NbtType.COMPOUND); + List newBlockEntityTag = craftData.getResultItems()[0].getTag().getList("Patterns", NbtType.COMPOUND); // Get the pattern that the Bedrock client requests - the last pattern in the Patterns list - NbtMap pattern = newblockEntityTag.get(newblockEntityTag.size() - 1); + NbtMap pattern = newBlockEntityTag.get(newBlockEntityTag.size() - 1); // Get the Java index of this pattern int index = PATTERN_TO_INDEX.getOrDefault(pattern.getString("Pattern"), -1); if (index == -1) { From c1f5380ed1777ebd717991832578a6847b320e10 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sat, 26 Dec 2020 12:20:59 -0500 Subject: [PATCH 18/39] Add horse inventory support --- .../animal/horse/AbstractHorseEntity.java | 7 + .../animal/horse/ChestedHorseEntity.java | 3 + .../living/animal/horse/LlamaEntity.java | 4 +- .../connector/inventory/AnvilContainer.java | 6 +- .../connector/inventory/BeaconContainer.java | 5 +- .../connector/inventory/Container.java | 5 +- .../inventory/EnchantingContainer.java | 5 +- .../connector/inventory/FurnaceInventory.java | 5 +- .../connector/inventory/Inventory.java | 11 +- .../inventory/MerchantContainer.java | 5 +- .../connector/inventory/PlayerInventory.java | 2 +- .../network/session/GeyserSession.java | 4 + .../BedrockItemStackRequestTranslator.java | 2 +- .../player/BedrockInteractTranslator.java | 24 ++- .../inventory/InventoryTranslator.java | 8 +- .../translators/AnvilInventoryTranslator.java | 2 +- .../translators/BaseInventoryTranslator.java | 2 +- .../BeaconInventoryTranslator.java | 2 +- .../EnchantingInventoryTranslator.java | 2 +- .../translators/LoomInventoryTranslator.java | 1 + .../MerchantInventoryTranslator.java | 2 +- .../StonecutterInventoryTranslator.java | 1 + .../AbstractHorseInventoryTranslator.java | 66 +++++++++ .../ChestedHorseInventoryTranslator.java | 112 ++++++++++++++ .../horse/DonkeyInventoryTranslator.java | 32 ++++ .../horse/HorseInventoryTranslator.java | 52 +++++++ .../horse/LlamaInventoryTranslator.java | 32 ++++ .../updater/HorseInventoryUpdater.java | 69 +++++++++ .../inventory/updater/InventoryUpdater.java | 1 - .../window/JavaCloseWindowTranslator.java | 5 +- .../window/JavaOpenHorseWindowTranslator.java | 139 ++++++++++++++++++ .../java/window/JavaOpenWindowTranslator.java | 3 +- .../java/window/JavaSetSlotTranslator.java | 2 +- .../window/JavaWindowItemsTranslator.java | 3 +- .../window/JavaWindowPropertyTranslator.java | 2 +- .../connector/utils/InventoryUtils.java | 5 +- 36 files changed, 572 insertions(+), 59 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/horse/AbstractHorseInventoryTranslator.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/horse/ChestedHorseInventoryTranslator.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/horse/DonkeyInventoryTranslator.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/horse/HorseInventoryTranslator.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/horse/LlamaInventoryTranslator.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/HorseInventoryUpdater.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaOpenHorseWindowTranslator.java diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/AbstractHorseEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/AbstractHorseEntity.java index cf9f84b4..8913da0d 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/AbstractHorseEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/AbstractHorseEntity.java @@ -30,6 +30,7 @@ import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityEventType; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; +import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; import com.nukkitx.protocol.bedrock.packet.EntityEventPacket; import org.geysermc.connector.entity.living.animal.AnimalEntity; import org.geysermc.connector.entity.type.EntityType; @@ -40,6 +41,9 @@ public class AbstractHorseEntity extends AnimalEntity { public AbstractHorseEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { super(entityId, geyserId, entityType, position, motion, rotation); + + // Specifies the size of the entity's inventory. Required to place slots in the entity. + metadata.put(EntityData.CONTAINER_BASE_SIZE, 2); } @Override @@ -75,6 +79,9 @@ public class AbstractHorseEntity extends AnimalEntity { entityEventPacket.setData(ItemRegistry.WHEAT.getBedrockId() << 16); session.sendUpstreamPacket(entityEventPacket); } + + // Set container type if tamed + metadata.put(EntityData.CONTAINER_TYPE, ((xd & 0x02) == 0x02) ? (byte) ContainerType.HORSE.getId() : (byte) 0); } // Needed to control horses diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/ChestedHorseEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/ChestedHorseEntity.java index 7343f5e8..7b78aabb 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/ChestedHorseEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/ChestedHorseEntity.java @@ -27,6 +27,7 @@ package org.geysermc.connector.entity.living.animal.horse; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; @@ -35,6 +36,8 @@ public class ChestedHorseEntity extends AbstractHorseEntity { public ChestedHorseEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { super(entityId, geyserId, entityType, position, motion, rotation); + + metadata.put(EntityData.CONTAINER_BASE_SIZE, 16); } @Override diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/LlamaEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/LlamaEntity.java index ddac4a63..d33624e2 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/LlamaEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/LlamaEntity.java @@ -38,6 +38,8 @@ public class LlamaEntity extends ChestedHorseEntity { public LlamaEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { super(entityId, geyserId, entityType, position, motion, rotation); + + metadata.put(EntityData.CONTAINER_STRENGTH_MODIFIER, 3); // Presumably 3 slots for every 1 strength } @Override @@ -56,7 +58,7 @@ public class LlamaEntity extends ChestedHorseEntity { // The damage value is the dye color that Java sends us // Always going to be a carpet so we can hardcode 171 in BlockTranslator // The int then short conversion is required or we get a ClassCastException - equipmentPacket.setChestplate(ItemData.of(BlockTranslator.CARPET, (short)((int) entityMetadata.getValue()), 1)); + equipmentPacket.setChestplate(ItemData.of(BlockTranslator.CARPET, (short) ((int) entityMetadata.getValue()), 1)); } else { equipmentPacket.setChestplate(ItemData.AIR); } diff --git a/connector/src/main/java/org/geysermc/connector/inventory/AnvilContainer.java b/connector/src/main/java/org/geysermc/connector/inventory/AnvilContainer.java index d940ac75..0b6482f2 100644 --- a/connector/src/main/java/org/geysermc/connector/inventory/AnvilContainer.java +++ b/connector/src/main/java/org/geysermc/connector/inventory/AnvilContainer.java @@ -25,10 +25,8 @@ package org.geysermc.connector.inventory; -import com.github.steveice10.mc.protocol.data.game.window.WindowType; - public class AnvilContainer extends Container { - public AnvilContainer(String title, int id, WindowType windowType, int size, PlayerInventory playerInventory) { - super(title, id, windowType, size, playerInventory); + public AnvilContainer(String title, int id, int size, PlayerInventory playerInventory) { + super(title, id, size, playerInventory); } } diff --git a/connector/src/main/java/org/geysermc/connector/inventory/BeaconContainer.java b/connector/src/main/java/org/geysermc/connector/inventory/BeaconContainer.java index 70ecd84f..267565b8 100644 --- a/connector/src/main/java/org/geysermc/connector/inventory/BeaconContainer.java +++ b/connector/src/main/java/org/geysermc/connector/inventory/BeaconContainer.java @@ -25,7 +25,6 @@ package org.geysermc.connector.inventory; -import com.github.steveice10.mc.protocol.data.game.window.WindowType; import lombok.Getter; import lombok.Setter; @@ -35,7 +34,7 @@ public class BeaconContainer extends Container { private int primaryId; private int secondaryId; - public BeaconContainer(String title, int id, WindowType windowType, int size, PlayerInventory playerInventory) { - super(title, id, windowType, size, playerInventory); + public BeaconContainer(String title, int id,int size, PlayerInventory playerInventory) { + super(title, id, size, playerInventory); } } diff --git a/connector/src/main/java/org/geysermc/connector/inventory/Container.java b/connector/src/main/java/org/geysermc/connector/inventory/Container.java index acf450e1..eb1d9c3d 100644 --- a/connector/src/main/java/org/geysermc/connector/inventory/Container.java +++ b/connector/src/main/java/org/geysermc/connector/inventory/Container.java @@ -25,7 +25,6 @@ package org.geysermc.connector.inventory; -import com.github.steveice10.mc.protocol.data.game.window.WindowType; import lombok.Getter; import lombok.NonNull; import org.geysermc.connector.network.translators.inventory.InventoryTranslator; @@ -38,8 +37,8 @@ public class Container extends Inventory { private final PlayerInventory playerInventory; private final int containerSize; - public Container(String title, int id, WindowType windowType, int size, PlayerInventory playerInventory) { - super(title, id, windowType, size); + public Container(String title, int id, int size, PlayerInventory playerInventory) { + super(title, id, size); this.playerInventory = playerInventory; this.containerSize = this.size + InventoryTranslator.PLAYER_INVENTORY_SIZE; } diff --git a/connector/src/main/java/org/geysermc/connector/inventory/EnchantingContainer.java b/connector/src/main/java/org/geysermc/connector/inventory/EnchantingContainer.java index c8b2bef1..ab0e544d 100644 --- a/connector/src/main/java/org/geysermc/connector/inventory/EnchantingContainer.java +++ b/connector/src/main/java/org/geysermc/connector/inventory/EnchantingContainer.java @@ -25,7 +25,6 @@ package org.geysermc.connector.inventory; -import com.github.steveice10.mc.protocol.data.game.window.WindowType; import com.nukkitx.protocol.bedrock.data.inventory.EnchantOptionData; import lombok.Getter; @@ -41,8 +40,8 @@ public class EnchantingContainer extends Container { @Getter private final GeyserEnchantOption[] geyserEnchantOptions; - public EnchantingContainer(String title, int id, WindowType windowType, int size, PlayerInventory playerInventory) { - super(title, id, windowType, size, playerInventory); + public EnchantingContainer(String title, int id, int size, PlayerInventory playerInventory) { + super(title, id, size, playerInventory); enchantOptions = new EnchantOptionData[3]; geyserEnchantOptions = new GeyserEnchantOption[3]; diff --git a/connector/src/main/java/org/geysermc/connector/inventory/FurnaceInventory.java b/connector/src/main/java/org/geysermc/connector/inventory/FurnaceInventory.java index 0af76244..6574374b 100644 --- a/connector/src/main/java/org/geysermc/connector/inventory/FurnaceInventory.java +++ b/connector/src/main/java/org/geysermc/connector/inventory/FurnaceInventory.java @@ -26,7 +26,6 @@ package org.geysermc.connector.inventory; -import com.github.steveice10.mc.protocol.data.game.window.WindowType; import lombok.Getter; import lombok.Setter; @@ -36,7 +35,7 @@ public class FurnaceInventory extends Inventory { @Setter private int test; - public FurnaceInventory(String title, int id, WindowType windowType, int size) { - super(title, id, windowType, size); + public FurnaceInventory(String title, int id, int size) { + super(title, id, size); } } diff --git a/connector/src/main/java/org/geysermc/connector/inventory/Inventory.java b/connector/src/main/java/org/geysermc/connector/inventory/Inventory.java index fed94b0b..14d80c38 100644 --- a/connector/src/main/java/org/geysermc/connector/inventory/Inventory.java +++ b/connector/src/main/java/org/geysermc/connector/inventory/Inventory.java @@ -25,7 +25,6 @@ package org.geysermc.connector.inventory; -import com.github.steveice10.mc.protocol.data.game.window.WindowType; import com.nukkitx.math.vector.Vector3i; import lombok.Getter; import lombok.NonNull; @@ -38,9 +37,6 @@ public class Inventory { @Getter protected int id; - @Getter - protected WindowType windowType; - @Getter protected final int size; @@ -64,14 +60,13 @@ public class Inventory { @Getter protected short transactionId = 0; - protected Inventory(int id, WindowType windowType, int size) { - this("Inventory", id, windowType, size); + protected Inventory(int id, int size) { + this("Inventory", id, size); } - protected Inventory(String title, int id, WindowType windowType, int size) { + protected Inventory(String title, int id, int size) { this.title = title; this.id = id; - this.windowType = windowType; this.size = size; this.items = new GeyserItemStack[size]; Arrays.fill(items, GeyserItemStack.EMPTY); diff --git a/connector/src/main/java/org/geysermc/connector/inventory/MerchantContainer.java b/connector/src/main/java/org/geysermc/connector/inventory/MerchantContainer.java index 03ae8ac3..a33f8147 100644 --- a/connector/src/main/java/org/geysermc/connector/inventory/MerchantContainer.java +++ b/connector/src/main/java/org/geysermc/connector/inventory/MerchantContainer.java @@ -27,7 +27,6 @@ package org.geysermc.connector.inventory; import com.github.steveice10.mc.protocol.data.game.window.VillagerTrade; -import com.github.steveice10.mc.protocol.data.game.window.WindowType; import lombok.Getter; import lombok.Setter; import org.geysermc.connector.entity.Entity; @@ -38,7 +37,7 @@ public class MerchantContainer extends Container { private Entity villager; private VillagerTrade[] villagerTrades; - public MerchantContainer(String title, int id, WindowType windowType, int size, PlayerInventory playerInventory) { - super(title, id, windowType, size, playerInventory); + public MerchantContainer(String title, int id, int size, PlayerInventory playerInventory) { + super(title, id, size, playerInventory); } } diff --git a/connector/src/main/java/org/geysermc/connector/inventory/PlayerInventory.java b/connector/src/main/java/org/geysermc/connector/inventory/PlayerInventory.java index e6aeb5ca..52066a80 100644 --- a/connector/src/main/java/org/geysermc/connector/inventory/PlayerInventory.java +++ b/connector/src/main/java/org/geysermc/connector/inventory/PlayerInventory.java @@ -45,7 +45,7 @@ public class PlayerInventory extends Inventory { private GeyserItemStack cursor = GeyserItemStack.EMPTY; public PlayerInventory() { - super(0, null, 46); + super(0, 46); heldItemSlot = 0; } diff --git a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java index 880e8712..df293630 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java @@ -83,6 +83,7 @@ import org.geysermc.connector.network.translators.EntityIdentifierRegistry; import org.geysermc.connector.network.translators.PacketTranslatorRegistry; import org.geysermc.connector.network.translators.chat.MessageTranslator; import org.geysermc.connector.network.translators.collision.CollisionManager; +import org.geysermc.connector.network.translators.inventory.InventoryTranslator; import org.geysermc.connector.network.translators.item.ItemRegistry; import org.geysermc.connector.skin.SkinManager; import org.geysermc.connector.utils.*; @@ -123,6 +124,9 @@ public class GeyserSession implements CommandSender { @Setter private Inventory openInventory; + @Setter + private InventoryTranslator inventoryTranslator = InventoryTranslator.PLAYER_INVENTORY_TRANSLATOR; + private final AtomicInteger itemNetId = new AtomicInteger(1); @Getter(AccessLevel.NONE) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockItemStackRequestTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockItemStackRequestTranslator.java index 1ab68106..65083dde 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockItemStackRequestTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockItemStackRequestTranslator.java @@ -43,7 +43,7 @@ public class BedrockItemStackRequestTranslator extends PacketTranslator translator.translateRequests(session, inventory, packet.getRequests())); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockInteractTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockInteractTranslator.java index a2739cd8..86f30c2f 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockInteractTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockInteractTranslator.java @@ -38,6 +38,7 @@ import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket; import com.nukkitx.protocol.bedrock.packet.InteractPacket; import lombok.Getter; import org.geysermc.connector.entity.Entity; +import org.geysermc.connector.entity.living.animal.horse.AbstractHorseEntity; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.inventory.GeyserItemStack; import org.geysermc.connector.network.session.GeyserSession; @@ -357,14 +358,23 @@ public class BedrockInteractTranslator extends PacketTranslator break; case OPEN_INVENTORY: if (session.getOpenInventory() == null) { - session.setOpenInventory(session.getPlayerInventory()); + Entity ridingEntity = session.getRidingVehicleEntity(); + if (ridingEntity instanceof AbstractHorseEntity) { + if (ridingEntity.getMetadata().getFlags().getFlag(EntityFlag.TAMED)) { + // We should request to open the horse inventory instead + ClientPlayerStatePacket openHorseWindowPacket = new ClientPlayerStatePacket((int)session.getPlayerEntity().getEntityId(), PlayerState.OPEN_HORSE_INVENTORY); + session.sendDownstreamPacket(openHorseWindowPacket); + } + } else { + session.setOpenInventory(session.getPlayerInventory()); - ContainerOpenPacket containerOpenPacket = new ContainerOpenPacket(); - containerOpenPacket.setId((byte) 0); - containerOpenPacket.setType(ContainerType.INVENTORY); - containerOpenPacket.setUniqueEntityId(-1); - containerOpenPacket.setBlockPosition(entity.getPosition().toInt()); - session.sendUpstreamPacket(containerOpenPacket); + ContainerOpenPacket containerOpenPacket = new ContainerOpenPacket(); + containerOpenPacket.setId((byte) 0); + containerOpenPacket.setType(ContainerType.INVENTORY); + containerOpenPacket.setUniqueEntityId(-1); + containerOpenPacket.setBlockPosition(entity.getPosition().toInt()); + session.sendUpstreamPacket(containerOpenPacket); + } } break; } 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 3e5bf0c6..dc09d922 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 @@ -60,10 +60,11 @@ import java.util.*; @AllArgsConstructor public abstract class InventoryTranslator { + public static final InventoryTranslator PLAYER_INVENTORY_TRANSLATOR = new PlayerInventoryTranslator(); public static final Map INVENTORY_TRANSLATORS = new HashMap() { { /* Player Inventory */ - put(null, new PlayerInventoryTranslator()); //player inventory + put(null, PLAYER_INVENTORY_TRANSLATOR); /* Chest UIs */ put(WindowType.GENERIC_9X1, new SingleChestInventoryTranslator(9)); @@ -95,9 +96,6 @@ public abstract class InventoryTranslator { /* Generics */ put(WindowType.GENERIC_3X3, new GenericBlockInventoryTranslator(9, "minecraft:dispenser[facing=north,triggered=false]", ContainerType.DISPENSER)); put(WindowType.HOPPER, new GenericBlockInventoryTranslator(5, "minecraft:hopper[enabled=false,facing=down]", ContainerType.HOPPER)); - - /* todo */ - // horse } }; @@ -193,7 +191,7 @@ public abstract class InventoryTranslator { int sourceSlot = bedrockSlotToJava(transferAction.getSource()); int destSlot = bedrockSlotToJava(transferAction.getDestination()); - if (shouldRejectItemPlace(session, inventory, isCursor(transferAction.getSource()) ? -1 :sourceSlot, destSlot)) { + if (shouldRejectItemPlace(session, inventory, isCursor(transferAction.getSource()) ? -1 : sourceSlot, destSlot)) { // This item would not be here in Java return rejectRequest(request, false); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/AnvilInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/AnvilInventoryTranslator.java index 27de329c..d4684057 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/AnvilInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/AnvilInventoryTranslator.java @@ -82,6 +82,6 @@ public class AnvilInventoryTranslator extends AbstractBlockInventoryTranslator { @Override public Inventory createInventory(String name, int windowId, WindowType windowType, PlayerInventory playerInventory) { - return new AnvilContainer(name, windowId, windowType, this.size, playerInventory); + return new AnvilContainer(name, windowId, this.size, playerInventory); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/BaseInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/BaseInventoryTranslator.java index 48d2ac10..32381d9b 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/BaseInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/BaseInventoryTranslator.java @@ -96,6 +96,6 @@ public abstract class BaseInventoryTranslator extends InventoryTranslator { @Override public Inventory createInventory(String name, int windowId, WindowType windowType, PlayerInventory playerInventory) { - return new Container(name, windowId, windowType, this.size, playerInventory); + return new Container(name, windowId, this.size, playerInventory); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/BeaconInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/BeaconInventoryTranslator.java index 8ffc3e1a..6407b9b3 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/BeaconInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/BeaconInventoryTranslator.java @@ -129,6 +129,6 @@ public class BeaconInventoryTranslator extends AbstractBlockInventoryTranslator @Override public Inventory createInventory(String name, int windowId, WindowType windowType, PlayerInventory playerInventory) { - return new BeaconContainer(name, windowId, windowType, this.size, playerInventory); + return new BeaconContainer(name, windowId, this.size, playerInventory); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/EnchantingInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/EnchantingInventoryTranslator.java index c8ae8e2d..50e8ec6a 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/EnchantingInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/EnchantingInventoryTranslator.java @@ -170,7 +170,7 @@ public class EnchantingInventoryTranslator extends AbstractBlockInventoryTransla @Override public Inventory createInventory(String name, int windowId, WindowType windowType, PlayerInventory playerInventory) { - return new EnchantingContainer(name, windowId, windowType, this.size, playerInventory); + return new EnchantingContainer(name, windowId, this.size, playerInventory); } /** diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LoomInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LoomInventoryTranslator.java index 47f21379..31388700 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LoomInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LoomInventoryTranslator.java @@ -143,6 +143,7 @@ public class LoomInventoryTranslator extends AbstractBlockInventoryTranslator { ClientClickWindowButtonPacket packet = new ClientClickWindowButtonPacket(inventory.getId(), index); System.out.println(packet); session.sendDownstreamPacket(packet); + GeyserItemStack inputCopy = inventory.getItem(0).copy(); inputCopy.setNetId(session.getItemNetId().incrementAndGet()); // Add the pattern manually, for better item synchronization diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/MerchantInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/MerchantInventoryTranslator.java index 33a4e824..c19a81be 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/MerchantInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/MerchantInventoryTranslator.java @@ -149,6 +149,6 @@ public class MerchantInventoryTranslator extends BaseInventoryTranslator { @Override public Inventory createInventory(String name, int windowId, WindowType windowType, PlayerInventory playerInventory) { - return new MerchantContainer(name, windowId, windowType, this.size, playerInventory); + return new MerchantContainer(name, windowId, this.size, playerInventory); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/StonecutterInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/StonecutterInventoryTranslator.java index 7bac3c65..42e09400 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/StonecutterInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/StonecutterInventoryTranslator.java @@ -57,6 +57,7 @@ public class StonecutterInventoryTranslator extends AbstractBlockInventoryTransl @Override public ItemStackResponsePacket.Response translateSpecialRequest(GeyserSession session, Inventory inventory, ItemStackRequestPacket.Request request) { // TODO: Also surely to change in the future + // TODO: don't spam the ClickWindowButtonPacket? StackRequestActionData data = request.getActions()[1]; if (!(data instanceof CraftResultsDeprecatedStackRequestActionData)) { return rejectRequest(request); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/horse/AbstractHorseInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/horse/AbstractHorseInventoryTranslator.java new file mode 100644 index 00000000..b33542d3 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/horse/AbstractHorseInventoryTranslator.java @@ -0,0 +1,66 @@ +/* + * 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.translators.horse; + +import org.geysermc.connector.inventory.Inventory; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.inventory.translators.BaseInventoryTranslator; +import org.geysermc.connector.network.translators.inventory.updater.HorseInventoryUpdater; +import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater; + +public abstract class AbstractHorseInventoryTranslator extends BaseInventoryTranslator { + private final InventoryUpdater updater; + + public AbstractHorseInventoryTranslator(int size) { + super(size); + this.updater = HorseInventoryUpdater.INSTANCE; + } + + @Override + public void prepareInventory(GeyserSession session, Inventory inventory) { + + } + + @Override + public void openInventory(GeyserSession session, Inventory inventory) { + + } + + @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); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/horse/ChestedHorseInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/horse/ChestedHorseInventoryTranslator.java new file mode 100644 index 00000000..65fc9dd7 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/horse/ChestedHorseInventoryTranslator.java @@ -0,0 +1,112 @@ +/* + * 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.translators.horse; + +import com.nukkitx.protocol.bedrock.data.inventory.ContainerId; +import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; +import com.nukkitx.protocol.bedrock.data.inventory.ItemData; +import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData; +import com.nukkitx.protocol.bedrock.packet.InventoryContentPacket; +import org.geysermc.connector.inventory.Inventory; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.inventory.BedrockContainerSlot; + +import java.util.Arrays; + +public abstract class ChestedHorseInventoryTranslator extends AbstractHorseInventoryTranslator { + private final int chestSize; + private final int equipSlot; + + /** + * @param size the total Java size of the inventory + * @param equipSlot the Java equipment slot. For + */ + public ChestedHorseInventoryTranslator(int size, int equipSlot) { + super(size); + this.chestSize = size - 2; + this.equipSlot = equipSlot; + } + + @Override + public int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData) { + if (slotInfoData.getContainer() == ContainerSlotType.HORSE_EQUIP) { + return this.equipSlot; + } + if (slotInfoData.getContainer() == ContainerSlotType.CONTAINER) { + return slotInfoData.getSlot() + 1; + } + return super.bedrockSlotToJava(slotInfoData); + } + + @Override + public BedrockContainerSlot javaSlotToBedrockContainer(int slot) { + if (slot == this.equipSlot) { + return new BedrockContainerSlot(ContainerSlotType.HORSE_EQUIP, 0); + } + if (slot <= this.size) { + return new BedrockContainerSlot(ContainerSlotType.CONTAINER, slot - 1); + } + return super.javaSlotToBedrockContainer(slot); + } + + @Override + public int javaSlotToBedrock(int slot) { + if (slot == 0 && this.equipSlot == 0) { + return 0; + } + if (slot <= this.size) { + return slot - 1; + } + return super.javaSlotToBedrock(slot); + } + + @Override + public void updateInventory(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] = inventory.getItem(this.size + i + offset).getItemData(session); + } + InventoryContentPacket contentPacket = new InventoryContentPacket(); + contentPacket.setContainerId(ContainerId.INVENTORY); + contentPacket.setContents(Arrays.asList(bedrockItems)); + session.sendUpstreamPacket(contentPacket); + + ItemData[] horseItems = new ItemData[chestSize + 1]; + // Manually specify the first slot - Java always has two slots (armor and saddle) and one is invisible. + // Bedrock doesn't have this invisible slot. + horseItems[0] = inventory.getItem(this.equipSlot).getItemData(session); + for (int i = 1; i < horseItems.length; i++) { + horseItems[i] = inventory.getItem(i + 1).getItemData(session); + } + + InventoryContentPacket llamaPacket = new InventoryContentPacket(); + llamaPacket.setContainerId(inventory.getId()); + llamaPacket.setContents(Arrays.asList(horseItems)); + System.out.println(llamaPacket); + session.sendUpstreamPacket(llamaPacket); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/horse/DonkeyInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/horse/DonkeyInventoryTranslator.java new file mode 100644 index 00000000..61a8d692 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/horse/DonkeyInventoryTranslator.java @@ -0,0 +1,32 @@ +/* + * 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.translators.horse; + +public class DonkeyInventoryTranslator extends ChestedHorseInventoryTranslator { + public DonkeyInventoryTranslator(int size) { + super(size, 0); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/horse/HorseInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/horse/HorseInventoryTranslator.java new file mode 100644 index 00000000..957933a0 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/horse/HorseInventoryTranslator.java @@ -0,0 +1,52 @@ +/* + * 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.translators.horse; + +import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; +import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData; +import org.geysermc.connector.network.translators.inventory.BedrockContainerSlot; + +public class HorseInventoryTranslator extends AbstractHorseInventoryTranslator { + public HorseInventoryTranslator(int size) { + super(size); + } + + @Override + public int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData) { + if (slotInfoData.getContainer() == ContainerSlotType.HORSE_EQUIP) { + return slotInfoData.getSlot(); + } + return super.bedrockSlotToJava(slotInfoData); + } + + @Override + public BedrockContainerSlot javaSlotToBedrockContainer(int slot) { + if (slot == 0 || slot == 1) { + return new BedrockContainerSlot(ContainerSlotType.HORSE_EQUIP, slot); + } + return super.javaSlotToBedrockContainer(slot); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/horse/LlamaInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/horse/LlamaInventoryTranslator.java new file mode 100644 index 00000000..523a1d3e --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/horse/LlamaInventoryTranslator.java @@ -0,0 +1,32 @@ +/* + * 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.translators.horse; + +public class LlamaInventoryTranslator extends ChestedHorseInventoryTranslator { + public LlamaInventoryTranslator(int size) { + super(size, 1); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/HorseInventoryUpdater.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/HorseInventoryUpdater.java new file mode 100644 index 00000000..2f88c49c --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/HorseInventoryUpdater.java @@ -0,0 +1,69 @@ +/* + * 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.inventory.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.inventory.InventoryTranslator; + +import java.util.Arrays; + +public class HorseInventoryUpdater extends InventoryUpdater { + public static final HorseInventoryUpdater INSTANCE = new HorseInventoryUpdater(); + + @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)] = inventory.getItem(i).getItemData(session); + } + + InventoryContentPacket contentPacket = new InventoryContentPacket(); + contentPacket.setContainerId(inventory.getId()); + contentPacket.setContents(Arrays.asList(bedrockItems)); + session.sendUpstreamPacket(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(4); // Horse GUI? + slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot)); + slotPacket.setItem(inventory.getItem(javaSlot).getItemData(session)); + session.sendUpstreamPacket(slotPacket); + System.out.println(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 index e7d14f13..d4026a23 100644 --- 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 @@ -32,7 +32,6 @@ 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.inventory.InventoryTranslator; -import org.geysermc.connector.network.translators.item.ItemTranslator; import java.util.Arrays; 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 68f1dda9..e84fd438 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 @@ -26,7 +26,6 @@ package org.geysermc.connector.network.translators.java.window; import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerCloseWindowPacket; -import org.geysermc.connector.inventory.Inventory; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; @@ -37,8 +36,6 @@ public class JavaCloseWindowTranslator extends PacketTranslator { - InventoryUtils.closeInventory(session, packet.getWindowId()); - }); + session.addInventoryTask(() -> InventoryUtils.closeInventory(session, packet.getWindowId())); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaOpenHorseWindowTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaOpenHorseWindowTranslator.java new file mode 100644 index 00000000..3d205d2a --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaOpenHorseWindowTranslator.java @@ -0,0 +1,139 @@ +/* + * 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.window; + +import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerOpenHorseWindowPacket; +import com.nukkitx.nbt.NbtMap; +import com.nukkitx.nbt.NbtMapBuilder; +import com.nukkitx.nbt.NbtType; +import com.nukkitx.protocol.bedrock.data.entity.EntityData; +import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; +import com.nukkitx.protocol.bedrock.packet.UpdateEquipPacket; +import org.geysermc.connector.entity.Entity; +import org.geysermc.connector.entity.living.animal.horse.ChestedHorseEntity; +import org.geysermc.connector.entity.living.animal.horse.LlamaEntity; +import org.geysermc.connector.inventory.Container; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.PacketTranslator; +import org.geysermc.connector.network.translators.Translator; +import org.geysermc.connector.network.translators.inventory.InventoryTranslator; +import org.geysermc.connector.network.translators.inventory.translators.horse.DonkeyInventoryTranslator; +import org.geysermc.connector.network.translators.inventory.translators.horse.HorseInventoryTranslator; +import org.geysermc.connector.network.translators.inventory.translators.horse.LlamaInventoryTranslator; +import org.geysermc.connector.utils.InventoryUtils; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +@Translator(packet = ServerOpenHorseWindowPacket.class) +public class JavaOpenHorseWindowTranslator extends PacketTranslator { + + private static final NbtMap ARMOR_SLOT; + private static final NbtMap CARPET_SLOT; + private static final NbtMap SADDLE_SLOT; + + static { + // Build the NBT mappings that Bedrock wants to lay out the GUI + String[] acceptedHorseArmorIdentifiers = new String[] {"minecraft:horsearmorleather", "minecraft:horsearmoriron", + "minecraft:horsearmorgold", "minecraft:horsearmordiamond"}; + NbtMapBuilder armorBuilder = NbtMap.builder(); + List acceptedArmors = new ArrayList<>(); + for (String identifier : acceptedHorseArmorIdentifiers) { + NbtMapBuilder acceptedItemBuilder = NbtMap.builder() + .putShort("Aux", Short.MAX_VALUE) + .putString("Name", identifier); + acceptedArmors.add(NbtMap.builder().putCompound("slotItem", acceptedItemBuilder.build()).build()); + } + armorBuilder.putList("acceptedItems", NbtType.COMPOUND, acceptedArmors); + NbtMapBuilder armorItem = NbtMap.builder() + .putShort("Aux", Short.MAX_VALUE) + .putString("Name", "minecraft:horsearmoriron"); + armorBuilder.putCompound("item", armorItem.build()); + armorBuilder.putInt("slotNumber", 1); + ARMOR_SLOT = armorBuilder.build(); + + NbtMapBuilder carpetBuilder = NbtMap.builder(); + NbtMapBuilder carpetItem = NbtMap.builder() + .putShort("Aux", Short.MAX_VALUE) + .putString("Name", "minecraft:carpet"); + List acceptedCarpet = Collections.singletonList(NbtMap.builder().putCompound("slotItem", carpetItem.build()).build()); + carpetBuilder.putList("acceptedItems", NbtType.COMPOUND, acceptedCarpet); + carpetBuilder.putCompound("item", carpetItem.build()); + carpetBuilder.putInt("slotNumber", 1); + CARPET_SLOT = carpetBuilder.build(); + + NbtMapBuilder saddleBuilder = NbtMap.builder(); + NbtMapBuilder acceptedSaddle = NbtMap.builder() + .putShort("Aux", Short.MAX_VALUE) + .putString("Name", "minecraft:saddle"); + List acceptedItem = Collections.singletonList(NbtMap.builder().putCompound("slotItem", acceptedSaddle.build()).build()); + saddleBuilder.putList("acceptedItems", NbtType.COMPOUND, acceptedItem); + saddleBuilder.putCompound("item", acceptedSaddle.build()); + saddleBuilder.putInt("slotNumber", 0); + SADDLE_SLOT = saddleBuilder.build(); + } + + @Override + public void translate(ServerOpenHorseWindowPacket packet, GeyserSession session) { + System.out.println(packet.toString()); + Entity entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId()); + if (entity == null) { + return; + } + + UpdateEquipPacket updateEquipPacket = new UpdateEquipPacket(); + updateEquipPacket.setWindowId((short) packet.getWindowId()); + updateEquipPacket.setWindowType((short) ContainerType.HORSE.getId()); + updateEquipPacket.setUniqueEntityId(entity.getGeyserId()); + + NbtMapBuilder builder = NbtMap.builder(); + List slots = new ArrayList<>(); + + InventoryTranslator inventoryTranslator; + if (entity instanceof LlamaEntity) { + inventoryTranslator = new LlamaInventoryTranslator(packet.getNumberOfSlots()); + slots.add(CARPET_SLOT); + } else if (entity instanceof ChestedHorseEntity) { + inventoryTranslator = new DonkeyInventoryTranslator(packet.getNumberOfSlots()); + slots.add(SADDLE_SLOT); + } else { + inventoryTranslator = new HorseInventoryTranslator(packet.getNumberOfSlots()); + slots.add(SADDLE_SLOT); + slots.add(ARMOR_SLOT); + } + + // Build the NbtMap that sets the icons for Bedrock (e.g. sets the saddle outline on the saddle slot) + builder.putList("slots", NbtType.COMPOUND, slots); + + updateEquipPacket.setTag(builder.build()); + System.out.println(updateEquipPacket); + session.sendUpstreamPacket(updateEquipPacket); + + session.setInventoryTranslator(inventoryTranslator); + InventoryUtils.openInventory(session, new Container(entity.getMetadata().getString(EntityData.NAMETAG), packet.getWindowId(), packet.getNumberOfSlots(), session.getPlayerInventory())); + } +} 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 89982dbb..d3201428 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 @@ -63,12 +63,13 @@ public class JavaOpenWindowTranslator extends PacketTranslator if (inventory == null) return; - InventoryTranslator translator = InventoryTranslator.INVENTORY_TRANSLATORS.get(inventory.getWindowType()); + InventoryTranslator translator = session.getInventoryTranslator(); if (translator != null) { GeyserItemStack newItem = GeyserItemStack.from(packet.getItem()); GeyserItemStack oldItem = inventory.getItem(packet.getSlot()); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaWindowItemsTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaWindowItemsTranslator.java index 26aab47b..7e88f067 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaWindowItemsTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaWindowItemsTranslator.java @@ -26,7 +26,6 @@ package org.geysermc.connector.network.translators.java.window; import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerWindowItemsPacket; -import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import org.geysermc.connector.inventory.GeyserItemStack; import org.geysermc.connector.inventory.Inventory; import org.geysermc.connector.network.session.GeyserSession; @@ -56,7 +55,7 @@ public class JavaWindowItemsTranslator extends PacketTranslator Date: Sat, 26 Dec 2020 13:24:50 -0500 Subject: [PATCH 19/39] Fix offset issue --- .../translators/horse/ChestedHorseInventoryTranslator.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/horse/ChestedHorseInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/horse/ChestedHorseInventoryTranslator.java index 65fc9dd7..2450f18d 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/horse/ChestedHorseInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/horse/ChestedHorseInventoryTranslator.java @@ -66,7 +66,7 @@ public abstract class ChestedHorseInventoryTranslator extends AbstractHorseInven if (slot == this.equipSlot) { return new BedrockContainerSlot(ContainerSlotType.HORSE_EQUIP, 0); } - if (slot <= this.size) { + if (slot <= this.size - 1) { // Accommodate for the lack of one slot (saddle or armor) return new BedrockContainerSlot(ContainerSlotType.CONTAINER, slot - 1); } return super.javaSlotToBedrockContainer(slot); @@ -77,7 +77,7 @@ public abstract class ChestedHorseInventoryTranslator extends AbstractHorseInven if (slot == 0 && this.equipSlot == 0) { return 0; } - if (slot <= this.size) { + if (slot <= this.size - 1) { return slot - 1; } return super.javaSlotToBedrock(slot); From 078af592491f2c0580030c63b8b11a1c73cfecf3 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sat, 26 Dec 2020 16:41:50 -0500 Subject: [PATCH 20/39] Streamline Item Net ID getting; cartography table finished --- .../connector/inventory/GeyserEnchantOption.java | 2 +- .../connector/network/session/GeyserSession.java | 13 +++++++++++-- .../bedrock/BedrockFilterTextTranslator.java | 2 +- .../translators/inventory/InventoryTranslator.java | 11 ++++++----- .../translators/CartographyInventoryTranslator.java | 9 ++++++--- .../translators/LoomInventoryTranslator.java | 5 ++--- .../translators/StonecutterInventoryTranslator.java | 2 +- .../horse/ChestedHorseInventoryTranslator.java | 13 +++++++------ .../network/translators/item/RecipeRegistry.java | 11 +++++++++++ .../java/JavaDeclareRecipesTranslator.java | 3 +++ .../java/window/JavaSetSlotTranslator.java | 4 ++-- .../java/window/JavaWindowItemsTranslator.java | 2 +- 12 files changed, 52 insertions(+), 25 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/inventory/GeyserEnchantOption.java b/connector/src/main/java/org/geysermc/connector/inventory/GeyserEnchantOption.java index ea58372e..ae4a9cf4 100644 --- a/connector/src/main/java/org/geysermc/connector/inventory/GeyserEnchantOption.java +++ b/connector/src/main/java/org/geysermc/connector/inventory/GeyserEnchantOption.java @@ -73,6 +73,6 @@ public class GeyserEnchantOption { } return new EnchantOptionData(xpCost, javaIndex + 16, EMPTY, Collections.singletonList(new EnchantData(bedrockEnchantIndex, enchantLevel)), EMPTY, - javaEnchantIndex == -1 ? "unknown" : ENCHANT_NAMES.get(javaEnchantIndex), session.getItemNetId().incrementAndGet()); + javaEnchantIndex == -1 ? "unknown" : ENCHANT_NAMES.get(javaEnchantIndex), session.getNextItemNetId()); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java index df293630..bbbdf99c 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java @@ -127,6 +127,10 @@ public class GeyserSession implements CommandSender { @Setter private InventoryTranslator inventoryTranslator = InventoryTranslator.PLAYER_INVENTORY_TRANSLATOR; + /** + * Use {@link #getNextItemNetId()} instead for consistency + */ + @Getter(AccessLevel.NONE) private final AtomicInteger itemNetId = new AtomicInteger(1); @Getter(AccessLevel.NONE) @@ -727,12 +731,10 @@ public class GeyserSession implements CommandSender { startGamePacket.setLevelName(serverName); startGamePacket.setPremiumWorldTemplateId("00000000-0000-0000-0000-000000000000"); - // startGamePacket.setCurrentTick(0); startGamePacket.setEnchantmentSeed(0); startGamePacket.setMultiplayerCorrelationId(""); startGamePacket.setItemEntries(ItemRegistry.ITEMS); startGamePacket.setVanillaVersion("*"); - // startGamePacket.setMovementServerAuthoritative(true); startGamePacket.setInventoriesServerAuthoritative(true); startGamePacket.setAuthoritativeMovementMode(AuthoritativeMovementMode.CLIENT); upstream.sendPacket(startGamePacket); @@ -773,6 +775,13 @@ public class GeyserSession implements CommandSender { } } + /** + * @return the next Bedrock item network ID to use for a new item + */ + public int getNextItemNetId() { + return itemNetId.getAndIncrement(); + } + public void addTeleport(TeleportCache teleportCache) { teleportMap.put(teleportCache.getTeleportConfirmId(), teleportCache); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockFilterTextTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockFilterTextTranslator.java index cf06acf5..438be6b0 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockFilterTextTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockFilterTextTranslator.java @@ -40,7 +40,7 @@ public class BedrockFilterTextTranslator extends PacketTranslator TIPPED_ARROW_RECIPES = new ObjectArrayList<>(); + /** + * Recipe data that, when sent to the client, enables cartography features. + * This does not have a Java equivalent. + */ + public static final List CARTOGRAPHY_RECIPE_DATA = new ObjectArrayList<>(); + /** * Recipe data that, when sent to the client, enables book cloning */ @@ -108,6 +114,11 @@ public class RecipeRegistry { MAP_EXTENDING_RECIPE_DATA = CraftingData.fromMulti(UUID.fromString("d392b075-4ba1-40ae-8789-af868d56f6ce"), LAST_RECIPE_NET_ID++); MAP_CLONING_RECIPE_DATA = CraftingData.fromMulti(UUID.fromString("85939755-ba10-4d9d-a4cc-efb7a8e943c4"), LAST_RECIPE_NET_ID++); BANNER_DUPLICATING_RECIPE_DATA = CraftingData.fromMulti(UUID.fromString("b5c5d105-75a2-4076-af2b-923ea2bf4bf0"), LAST_RECIPE_NET_ID++); + + CARTOGRAPHY_RECIPE_DATA.add(CraftingData.fromMulti(UUID.fromString("8b36268c-1829-483c-a0f1-993b7156a8f2"), LAST_RECIPE_NET_ID++)); // Map extending + CARTOGRAPHY_RECIPE_DATA.add(CraftingData.fromMulti(UUID.fromString("442d85ed-8272-4543-a6f1-418f90ded05d"), LAST_RECIPE_NET_ID++)); // Map cloning + CARTOGRAPHY_RECIPE_DATA.add(CraftingData.fromMulti(UUID.fromString("98c84b38-1085-46bd-b1ce-dd38c159e6cc"), LAST_RECIPE_NET_ID++)); // Map upgrading + CARTOGRAPHY_RECIPE_DATA.add(CraftingData.fromMulti(UUID.fromString("602234e4-cac1-4353-8bb7-b1ebff70024b"), LAST_RECIPE_NET_ID++)); // Map locking // https://github.com/pmmp/PocketMine-MP/blob/stable/src/pocketmine/inventory/MultiRecipe.php // Get all recipes that are not directly sent from a Java server 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 e74a8232..0c459133 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 @@ -149,6 +149,9 @@ public class JavaDeclareRecipesTranslator extends PacketTranslator stonecutterRecipeMap = new Int2ObjectOpenHashMap<>(); 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 11dc7a48..5d311fe3 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 @@ -47,7 +47,7 @@ public class JavaSetSlotTranslator extends PacketTranslator if (newItem.getItemData(session).equals(oldItem.getItemData(session))) { newItem.setNetId(oldItem.getNetId()); } else { - newItem.setNetId(session.getItemNetId().getAndIncrement()); + newItem.setNetId(session.getNextItemNetId()); } session.getPlayerInventory().setCursor(newItem); InventoryUtils.updateCursor(session); @@ -67,7 +67,7 @@ public class JavaSetSlotTranslator extends PacketTranslator newItem.setNetId(oldItem.getNetId()); System.out.println("OLD: " + newItem.getNetId()); } else { - newItem.setNetId(session.getItemNetId().getAndIncrement()); + newItem.setNetId(session.getNextItemNetId()); System.out.println("NEW: " + newItem.getNetId()); } inventory.setItem(packet.getSlot(), newItem); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaWindowItemsTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaWindowItemsTranslator.java index 7e88f067..b87f1de8 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaWindowItemsTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaWindowItemsTranslator.java @@ -50,7 +50,7 @@ public class JavaWindowItemsTranslator extends PacketTranslator Date: Sat, 26 Dec 2020 19:16:35 -0500 Subject: [PATCH 21/39] Cleanup and delete FilterTextPacket - PRing to the main branch --- .../connector/inventory/BeaconContainer.java | 2 +- .../bedrock/BedrockFilterTextTranslator.java | 47 ------------------- .../item/translators/BannerTranslator.java | 2 - 3 files changed, 1 insertion(+), 50 deletions(-) delete mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockFilterTextTranslator.java diff --git a/connector/src/main/java/org/geysermc/connector/inventory/BeaconContainer.java b/connector/src/main/java/org/geysermc/connector/inventory/BeaconContainer.java index 267565b8..768a4966 100644 --- a/connector/src/main/java/org/geysermc/connector/inventory/BeaconContainer.java +++ b/connector/src/main/java/org/geysermc/connector/inventory/BeaconContainer.java @@ -34,7 +34,7 @@ public class BeaconContainer extends Container { private int primaryId; private int secondaryId; - public BeaconContainer(String title, int id,int size, PlayerInventory playerInventory) { + public BeaconContainer(String title, int id, int size, PlayerInventory playerInventory) { super(title, id, size, playerInventory); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockFilterTextTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockFilterTextTranslator.java deleted file mode 100644 index 438be6b0..00000000 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockFilterTextTranslator.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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.bedrock; - -import com.nukkitx.protocol.bedrock.packet.FilterTextPacket; -import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.PacketTranslator; -import org.geysermc.connector.network.translators.Translator; - -/** - * Used to send strings to the client and filter out unwanted words. - * Java doesn't care, so we don't care, and we approve all strings. - */ -@Translator(packet = FilterTextPacket.class) -public class BedrockFilterTextTranslator extends PacketTranslator { - - @Override - public void translate(FilterTextPacket packet, GeyserSession session) { - // TODO: Bedrock doesn't send this. Why? - session.getConnector().getLogger().error(packet.toString()); - packet.setFromServer(true); - session.sendUpstreamPacket(packet); - } -} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/BannerTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/BannerTranslator.java index 1870a127..d5f5c610 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/BannerTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/BannerTranslator.java @@ -32,7 +32,6 @@ import com.nukkitx.nbt.NbtMap; import com.nukkitx.nbt.NbtMapBuilder; import com.nukkitx.nbt.NbtType; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; -import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.network.translators.ItemRemapper; import org.geysermc.connector.network.translators.item.ItemEntry; import org.geysermc.connector.network.translators.item.ItemRegistry; @@ -182,7 +181,6 @@ public class BannerTranslator extends ItemTranslator { @Override public ItemStack translateToJava(ItemData itemData, ItemEntry itemEntry) { - GeyserConnector.getInstance().getLogger().warning(itemEntry.toString()); if (itemData.getTag() == null) { return super.translateToJava(itemData, itemEntry); } From 60da3b94320d4e751837e487f40b21a2eabb7ef4 Mon Sep 17 00:00:00 2001 From: AJ Ferguson Date: Sat, 26 Dec 2020 18:44:48 -0900 Subject: [PATCH 22/39] Temp slot --- .../inventory/InventoryTranslator.java | 88 +++++++++++++++++-- 1 file changed, 79 insertions(+), 9 deletions(-) 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 dc36ed17..3a201cdc 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 @@ -300,17 +300,33 @@ public abstract class InventoryTranslator { if (!InventoryUtils.canStack(cursor, plan.getItem(sourceSlot))) { //doesn't make sense, reject return rejectRequest(request); } - if (transferAction.getCount() != sourceAmount) { //TODO: handle partially picking up into non-empty cursor (temp slot) - return rejectRequest(request); + if (transferAction.getCount() != sourceAmount) { + int tempSlot = findTempSlot(inventory, cursor, false, sourceSlot); + if (tempSlot == -1) { + return rejectRequest(request); + } + plan.add(Click.LEFT, tempSlot); //place cursor into temp slot + plan.add(Click.LEFT, sourceSlot); //pickup source items into cursor + for (int i = 0; i < transferAction.getCount(); i++) { + plan.add(Click.RIGHT, tempSlot); //partially transfer source items into temp slot (original cursor) + } + plan.add(Click.LEFT, sourceSlot); //return remaining source items + plan.add(Click.LEFT, tempSlot); //retrieve original cursor items from temp slot + } else { + if (getSlotType(sourceSlot).equals(SlotType.NORMAL)) { + plan.add(Click.LEFT, sourceSlot); //release cursor onto source slot + } + plan.add(Click.LEFT, sourceSlot); //pickup combined cursor and source } - if (getSlotType(sourceSlot).equals(SlotType.NORMAL)) { - plan.add(Click.LEFT, sourceSlot); //release cursor onto source slot - } - plan.add(Click.LEFT, sourceSlot); //pickup combined cursor and source } } else { //transfer from one slot to another - if (!cursor.isEmpty()) { //TODO: handle slot transfer when cursor is already in use (temp slot) - return rejectRequest(request); + int tempSlot = -1; + if (!cursor.isEmpty()) { + tempSlot = findTempSlot(inventory, cursor, false, sourceSlot, destSlot); + if (tempSlot == -1) { + return rejectRequest(request); + } + plan.add(Click.LEFT, tempSlot); //place cursor into temp slot } int sourceAmount = plan.getItem(sourceSlot).getAmount(); if (transferAction.getCount() == sourceAmount) { //transfer all @@ -318,7 +334,7 @@ public abstract class InventoryTranslator { plan.add(Click.LEFT, destSlot); //let go of all items and done } else { //transfer some //try to transfer items with least clicks possible - int halfSource = sourceAmount / 2; //smaller half + int halfSource = sourceAmount - (sourceAmount / 2); //larger half int holding; if (transferAction.getCount() <= halfSource) { //faster to take only half plan.add(Click.RIGHT, sourceSlot); @@ -339,6 +355,9 @@ public abstract class InventoryTranslator { plan.add(Click.LEFT, sourceSlot); //return extra items to source slot } } + if (tempSlot != -1) { + plan.add(Click.LEFT, tempSlot); //retrieve original cursor + } } break; } @@ -724,11 +743,62 @@ public abstract class InventoryTranslator { public boolean checkNetId(GeyserSession session, Inventory inventory, StackRequestSlotInfoData slotInfoData) { if (slotInfoData.getStackNetworkId() < 0) return true; + if (slotInfoData.getContainer() == ContainerSlotType.CURSOR) //TODO: temporary + return true; GeyserItemStack currentItem = isCursor(slotInfoData) ? session.getPlayerInventory().getCursor() : inventory.getItem(bedrockSlotToJava(slotInfoData)); return currentItem.getNetId() == slotInfoData.getStackNetworkId(); } + /** + * Try to find a slot that can temporarily store the given item. + * Only looks in the main inventory and hotbar (excluding offhand). + * Only slots that are empty or contain a different type of item are valid. + * + * @return java id for the temporary slot, or -1 if no viable slot was found + */ + //TODO: compatibility for simulated inventory (ClickPlan) + private static int findTempSlot(Inventory inventory, GeyserItemStack item, boolean emptyOnly, int... slotBlacklist) { + int offset = inventory.getId() == 0 ? 1 : 0; //offhand is not a viable temp slot + HashSet itemBlacklist = new HashSet<>(slotBlacklist.length + 1); + itemBlacklist.add(item); + + IntSet potentialSlots = new IntOpenHashSet(36); + for (int i = inventory.getSize() - (36 + offset); i < inventory.getSize() - offset; i++) { + potentialSlots.add(i); + } + for (int i : slotBlacklist) { + potentialSlots.remove(i); + GeyserItemStack blacklistedItem = inventory.getItem(i); + if (!blacklistedItem.isEmpty()) { + itemBlacklist.add(blacklistedItem); + } + } + + for (int i : potentialSlots) { + GeyserItemStack testItem = inventory.getItem(i); + if ((emptyOnly && !testItem.isEmpty())) { + continue; + } + + boolean viable = true; + for (GeyserItemStack blacklistedItem : itemBlacklist) { + if (InventoryUtils.canStack(testItem, blacklistedItem)) { + viable = false; + break; + } + } + if (!viable) { + continue; + } + + System.out.println("TEMP SLOT CHOSEN: " + i + " => " + inventory.getItem(i)); + return i; + } + //could not find a viable temp slot + return -1; + } + public List makeContainerEntries(GeyserSession session, Inventory inventory, Set affectedSlots) { Map> containerMap = new HashMap<>(); for (int slot : affectedSlots) { From 956d264c3e68e8cb29281df29eb5bd59961615bb Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sat, 26 Dec 2020 22:55:14 -0500 Subject: [PATCH 23/39] Anvil renaming works; other things --- .../connector/inventory/AnvilContainer.java | 3 ++ .../inventory/CartographyContainer.java | 32 +++++++++++++++++++ .../bedrock/BedrockFilterTextTranslator.java | 14 ++++++++ .../inventory/InventoryTranslator.java | 20 +++++++++++- .../CartographyInventoryTranslator.java | 13 +++++--- 5 files changed, 77 insertions(+), 5 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/inventory/CartographyContainer.java diff --git a/connector/src/main/java/org/geysermc/connector/inventory/AnvilContainer.java b/connector/src/main/java/org/geysermc/connector/inventory/AnvilContainer.java index 0b6482f2..aba360a0 100644 --- a/connector/src/main/java/org/geysermc/connector/inventory/AnvilContainer.java +++ b/connector/src/main/java/org/geysermc/connector/inventory/AnvilContainer.java @@ -25,6 +25,9 @@ package org.geysermc.connector.inventory; +/** + * Used to determine if rename packets should be sent. + */ public class AnvilContainer extends Container { public AnvilContainer(String title, int id, int size, PlayerInventory playerInventory) { super(title, id, size, playerInventory); diff --git a/connector/src/main/java/org/geysermc/connector/inventory/CartographyContainer.java b/connector/src/main/java/org/geysermc/connector/inventory/CartographyContainer.java new file mode 100644 index 00000000..7e6d1cc7 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/inventory/CartographyContainer.java @@ -0,0 +1,32 @@ +/* + * 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.inventory; + +public class CartographyContainer extends Container { + public CartographyContainer(String title, int id, int size, PlayerInventory playerInventory) { + super(title, id, size, playerInventory); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockFilterTextTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockFilterTextTranslator.java index 8c963076..a7400ff0 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockFilterTextTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockFilterTextTranslator.java @@ -25,7 +25,10 @@ package org.geysermc.connector.network.translators.bedrock; +import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientRenameItemPacket; import com.nukkitx.protocol.bedrock.packet.FilterTextPacket; +import org.geysermc.connector.inventory.AnvilContainer; +import org.geysermc.connector.inventory.CartographyContainer; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; @@ -39,7 +42,18 @@ public class BedrockFilterTextTranslator extends PacketTranslator Date: Sun, 27 Dec 2020 11:08:48 -0500 Subject: [PATCH 24/39] Add TODO --- .../network/translators/inventory/InventoryTranslator.java | 1 + 1 file changed, 1 insertion(+) 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 b960f09e..95516dc2 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 @@ -462,6 +462,7 @@ public abstract class InventoryTranslator { } case DESTROY: { // Only called when a creative client wants to destroy an item... I think - Camotoy + //TODO there is a Count here we don't use DestroyStackRequestActionData destroyAction = (DestroyStackRequestActionData) action; if (!session.getGameMode().equals(GameMode.CREATIVE)) { // If this happens, let's throw an error and figure out why. From 2265de3ae90132148aecaa48a5547f4a7f08a2eb Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Mon, 28 Dec 2020 00:29:27 -0500 Subject: [PATCH 25/39] lecterns --- .../platform/spigot/GeyserSpigotPlugin.java | 10 +- .../GeyserSpigot1_12NativeWorldManager.java | 4 +- .../manager/GeyserSpigot1_12WorldManager.java | 5 +- .../GeyserSpigotFallbackWorldManager.java | 5 +- .../GeyserSpigotLegacyNativeWorldManager.java | 2 +- .../GeyserSpigotNativeWorldManager.java | 5 +- .../manager/GeyserSpigotWorldManager.java | 70 +++++++- .../connector/inventory/LecternContainer.java | 44 +++++ .../network/session/GeyserSession.java | 10 ++ .../BedrockContainerCloseTranslator.java | 2 +- .../BedrockLecternUpdateTranslator.java | 95 +++++++++++ .../inventory/InventoryTranslator.java | 3 + .../LecternInventoryTranslator.java | 159 ++++++++++++++++++ .../java/world/JavaUnloadChunkTranslator.java | 11 +- .../translators/world/GeyserWorldManager.java | 26 +++ .../translators/world/WorldManager.java | 27 +++ .../world/block/BlockStateValues.java | 10 ++ .../geysermc/connector/utils/ChunkUtils.java | 33 +++- .../connector/utils/DimensionUtils.java | 1 + 19 files changed, 502 insertions(+), 20 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/inventory/LecternContainer.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockLecternUpdateTranslator.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LecternInventoryTranslator.java diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotPlugin.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotPlugin.java index 39d4f993..c63c65d3 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotPlugin.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotPlugin.java @@ -157,14 +157,14 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { if (isViaVersion && isViaVersionNeeded()) { if (isLegacy) { // Pre-1.13 - this.geyserWorldManager = new GeyserSpigot1_12NativeWorldManager(); + this.geyserWorldManager = new GeyserSpigot1_12NativeWorldManager(this); } else { // Post-1.13 this.geyserWorldManager = new GeyserSpigotLegacyNativeWorldManager(this, use3dBiomes); } } else { // No ViaVersion - this.geyserWorldManager = new GeyserSpigotNativeWorldManager(use3dBiomes); + this.geyserWorldManager = new GeyserSpigotNativeWorldManager(this, use3dBiomes); } geyserLogger.debug("Using NMS adapter: " + this.geyserWorldManager.getClass() + ", " + nmsVersion); } catch (Exception e) { @@ -180,13 +180,13 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { // No NMS adapter if (isLegacy && isViaVersion) { // Use ViaVersion for converting pre-1.13 block states - this.geyserWorldManager = new GeyserSpigot1_12WorldManager(); + this.geyserWorldManager = new GeyserSpigot1_12WorldManager(this); } else if (isLegacy) { // Not sure how this happens - without ViaVersion, we don't know any block states, so just assume everything is air - this.geyserWorldManager = new GeyserSpigotFallbackWorldManager(); + this.geyserWorldManager = new GeyserSpigotFallbackWorldManager(this); } else { // Post-1.13 - this.geyserWorldManager = new GeyserSpigotWorldManager(use3dBiomes); + this.geyserWorldManager = new GeyserSpigotWorldManager(this, use3dBiomes); } geyserLogger.debug("Using default world manager: " + this.geyserWorldManager.getClass()); } diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigot1_12NativeWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigot1_12NativeWorldManager.java index f58b75cd..67e114c4 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigot1_12NativeWorldManager.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigot1_12NativeWorldManager.java @@ -27,6 +27,7 @@ package org.geysermc.platform.spigot.world.manager; import org.bukkit.Bukkit; import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; import org.geysermc.adapters.spigot.SpigotAdapters; import org.geysermc.adapters.spigot.SpigotWorldAdapter; import org.geysermc.connector.network.session.GeyserSession; @@ -40,7 +41,8 @@ import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.storage.BlockStorage; public class GeyserSpigot1_12NativeWorldManager extends GeyserSpigot1_12WorldManager { private final SpigotWorldAdapter adapter; - public GeyserSpigot1_12NativeWorldManager() { + public GeyserSpigot1_12NativeWorldManager(Plugin plugin) { + super(plugin); this.adapter = SpigotAdapters.getWorldAdapter(); // Unlike post-1.13, we can't build up a cache of block states, because block entities need some special conversion } diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigot1_12WorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigot1_12WorldManager.java index b00ddafa..881dece2 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigot1_12WorldManager.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigot1_12WorldManager.java @@ -30,6 +30,7 @@ import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.world.block.BlockTranslator; import us.myles.ViaVersion.api.Pair; @@ -61,8 +62,8 @@ public class GeyserSpigot1_12WorldManager extends GeyserSpigotWorldManager { */ private final List> protocolList; - public GeyserSpigot1_12WorldManager() { - super(false); + public GeyserSpigot1_12WorldManager(Plugin plugin) { + super(plugin, false); this.mappingData1_12to1_13 = ProtocolRegistry.getProtocol(Protocol1_13To1_12_2.class).getMappingData(); this.protocolList = ProtocolRegistry.getProtocolPath(CLIENT_PROTOCOL_VERSION, ProtocolVersion.v1_13.getVersion()); diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotFallbackWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotFallbackWorldManager.java index 49c675a1..dc0146ba 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotFallbackWorldManager.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotFallbackWorldManager.java @@ -26,6 +26,7 @@ package org.geysermc.platform.spigot.world.manager; import com.github.steveice10.mc.protocol.data.game.chunk.Chunk; +import org.bukkit.plugin.Plugin; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.world.block.BlockTranslator; @@ -35,9 +36,9 @@ import org.geysermc.connector.network.translators.world.block.BlockTranslator; * If this occurs to you somehow, please let us know!! */ public class GeyserSpigotFallbackWorldManager extends GeyserSpigotWorldManager { - public GeyserSpigotFallbackWorldManager() { + public GeyserSpigotFallbackWorldManager(Plugin plugin) { // Since this is pre-1.13 (and thus pre-1.15), there will never be 3D biomes. - super(false); + super(plugin, false); } @Override diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotLegacyNativeWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotLegacyNativeWorldManager.java index dec9b414..376df3fd 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotLegacyNativeWorldManager.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotLegacyNativeWorldManager.java @@ -47,7 +47,7 @@ public class GeyserSpigotLegacyNativeWorldManager extends GeyserSpigotNativeWorl private final Int2IntMap oldToNewBlockId; public GeyserSpigotLegacyNativeWorldManager(GeyserSpigotPlugin plugin, boolean use3dBiomes) { - super(use3dBiomes); + super(plugin, use3dBiomes); IntList allBlockStates = adapter.getAllBlockStates(); oldToNewBlockId = new Int2IntOpenHashMap(allBlockStates.size()); ProtocolVersion serverVersion = plugin.getServerProtocolVersion(); diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotNativeWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotNativeWorldManager.java index f703ecdb..a23fc7b5 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotNativeWorldManager.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotNativeWorldManager.java @@ -27,6 +27,7 @@ package org.geysermc.platform.spigot.world.manager; import org.bukkit.Bukkit; import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; import org.geysermc.adapters.spigot.SpigotAdapters; import org.geysermc.adapters.spigot.SpigotWorldAdapter; import org.geysermc.connector.network.session.GeyserSession; @@ -35,8 +36,8 @@ import org.geysermc.connector.network.translators.world.block.BlockTranslator; public class GeyserSpigotNativeWorldManager extends GeyserSpigotWorldManager { protected final SpigotWorldAdapter adapter; - public GeyserSpigotNativeWorldManager(boolean use3dBiomes) { - super(use3dBiomes); + public GeyserSpigotNativeWorldManager(Plugin plugin, boolean use3dBiomes) { + super(plugin, use3dBiomes); adapter = SpigotAdapters.getWorldAdapter(); } diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotWorldManager.java index cd1774ba..9b765fd6 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotWorldManager.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotWorldManager.java @@ -28,23 +28,35 @@ package org.geysermc.platform.spigot.world.manager; import com.fasterxml.jackson.databind.JsonNode; import com.github.steveice10.mc.protocol.MinecraftConstants; import com.github.steveice10.mc.protocol.data.game.chunk.Chunk; +import com.nukkitx.math.vector.Vector3i; +import com.nukkitx.nbt.NbtMap; +import com.nukkitx.nbt.NbtMapBuilder; +import com.nukkitx.nbt.NbtType; import it.unimi.dsi.fastutil.ints.Int2IntMap; import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.block.Biome; import org.bukkit.block.Block; +import org.bukkit.block.Lectern; import org.bukkit.block.data.BlockData; import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.BookMeta; +import org.bukkit.plugin.Plugin; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.inventory.translators.LecternInventoryTranslator; import org.geysermc.connector.network.translators.world.GeyserWorldManager; import org.geysermc.connector.network.translators.world.block.BlockTranslator; +import org.geysermc.connector.utils.BlockEntityUtils; import org.geysermc.connector.utils.FileUtils; import org.geysermc.connector.utils.GameRule; import org.geysermc.connector.utils.LanguageUtils; import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; /** * The base world manager to use when there is no supported NMS revision @@ -72,8 +84,11 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager { */ private final Int2IntMap biomeToIdMap = new Int2IntOpenHashMap(Biome.values().length); - public GeyserSpigotWorldManager(boolean use3dBiomes) { + private final Plugin plugin; + + public GeyserSpigotWorldManager(Plugin plugin, boolean use3dBiomes) { this.use3dBiomes = use3dBiomes; + this.plugin = plugin; // Load the values into the biome-to-ID map InputStream biomeStream = FileUtils.getResource("biomes.json"); @@ -132,9 +147,6 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager { @Override @SuppressWarnings("deprecation") public int[] getBiomeDataAt(GeyserSession session, int x, int z) { - if (session.getPlayerEntity() == null) { - return new int[1024]; - } int[] biomeData = new int[1024]; World world = Bukkit.getPlayer(session.getPlayerEntity().getUsername()).getWorld(); int chunkX = x << 4; @@ -167,6 +179,56 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager { return biomeData; } + @Override + public NbtMap getLecternDataAt(GeyserSession session, int x, int y, int z, boolean isChunkLoad) { + // Run as a task to prevent async issues + Bukkit.getScheduler().runTask(this.plugin, () -> { + Player bukkitPlayer; + if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) { + return; + } + Block block = bukkitPlayer.getWorld().getBlockAt(x, y, z); + if (!(block.getState() instanceof Lectern)) { + session.getConnector().getLogger().error("Lectern expected at: " + Vector3i.from(x, y, z).toString() + " but was not! " + block.toString()); + return; + } + Lectern lectern = (Lectern) block.getState(); + ItemStack itemStack = lectern.getInventory().getItem(0); + if (itemStack == null || !(itemStack.getItemMeta() instanceof BookMeta)) { + if (!isChunkLoad) { + // We need to update the lectern since it's not going to be updated otherwise + BlockEntityUtils.updateBlockEntity(session, LecternInventoryTranslator.getBaseLecternTag(x, y, z, 0).build(), Vector3i.from(x, y, z)); + } + // We don't care; return + return; + } + BookMeta bookMeta = (BookMeta) itemStack.getItemMeta(); + NbtMapBuilder lecternTag = LecternInventoryTranslator.getBaseLecternTag(x, y, z, bookMeta.getPageCount()); + lecternTag.putInt("page", lectern.getPage() / 2); + NbtMapBuilder bookTag = NbtMap.builder() + .putByte("Count", (byte) itemStack.getAmount()) + .putShort("Damage", (short) 0) + .putString("Name", "minecraft:writable_book"); + List pages = new ArrayList<>(); + for (String page : bookMeta.getPages()) { + NbtMapBuilder pageBuilder = NbtMap.builder() + .putString("photoname", "") + .putString("text", page); + pages.add(pageBuilder.build()); + } + bookTag.putCompound("tag", NbtMap.builder().putList("pages", NbtType.COMPOUND, pages).build()); + lecternTag.putCompound("book", bookTag.build()); + NbtMap blockEntityTag = lecternTag.build(); + BlockEntityUtils.updateBlockEntity(session, blockEntityTag, Vector3i.from(x, y, z)); + }); + return LecternInventoryTranslator.getBaseLecternTag(x, y, z, 0).build(); // Will be updated later + } + + @Override + public boolean shouldExpectLecternHandled() { + return true; + } + public Boolean getGameRuleBool(GeyserSession session, GameRule gameRule) { return Boolean.parseBoolean(Bukkit.getPlayer(session.getPlayerEntity().getUsername()).getWorld().getGameRuleValue(gameRule.getJavaID())); } diff --git a/connector/src/main/java/org/geysermc/connector/inventory/LecternContainer.java b/connector/src/main/java/org/geysermc/connector/inventory/LecternContainer.java new file mode 100644 index 00000000..0ce9217d --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/inventory/LecternContainer.java @@ -0,0 +1,44 @@ +/* + * 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.inventory; + +import com.nukkitx.math.vector.Vector3i; +import com.nukkitx.nbt.NbtMap; +import lombok.Getter; +import lombok.Setter; + +public class LecternContainer extends Container { + @Getter @Setter + private int currentBedrockPage = 0; + @Getter @Setter + private NbtMap blockEntityTag; + @Getter @Setter + private Vector3i position; + + public LecternContainer(String title, int id, int size, PlayerInventory playerInventory) { + super(title, id, size, playerInventory); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java index 436887d3..f00e9f0b 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java @@ -153,6 +153,16 @@ public class GeyserSession implements CommandSender { */ private final Object2LongMap itemFrameCache = new Object2LongOpenHashMap<>(); + /** + * Stores a list of all lectern locations and their block entity tags. + * See {@link org.geysermc.connector.network.translators.world.WorldManager#getLecternDataAt(GeyserSession, int, int, int, boolean)} + * for more information. + */ + private final List lecternCache = new ArrayList<>(); + + @Setter + private boolean droppingLecternBook; + @Setter private Vector2i lastChunkPosition = null; private int renderDistance; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockContainerCloseTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockContainerCloseTranslator.java index 5571ff8b..93837c20 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockContainerCloseTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockContainerCloseTranslator.java @@ -50,7 +50,7 @@ public class BedrockContainerCloseTranslator extends PacketTranslator { + + @Override + public void translate(LecternUpdatePacket packet, GeyserSession session) { + session.getConnector().getLogger().error(packet.toString()); + if (packet.isDroppingBook()) { + // Bedrock drops the book outside of the GUI. Java drops it in the GUI + // So, we enter the GUI and then drop it! :) + session.setDroppingLecternBook(true); + + Vector3f diff = session.getPlayerEntity().getPosition().sub(packet.getBlockPosition().toFloat()); + System.out.println(diff); + // Emulate an interact packet + ClientPlayerPlaceBlockPacket blockPacket = new ClientPlayerPlaceBlockPacket( + new Position(packet.getBlockPosition().getX(), packet.getBlockPosition().getY(), packet.getBlockPosition().getZ()), + BlockFace.values()[0], + Hand.MAIN_HAND, + packet.getBlockPosition().getX(), packet.getBlockPosition().getY(), packet.getBlockPosition().getZ(), //TODO + false); + session.sendDownstreamPacket(blockPacket); + } else { + // Bedrock wants to either move a page or exit + LecternContainer lecternContainer = (LecternContainer) session.getOpenInventory(); + if (lecternContainer.getCurrentBedrockPage() == packet.getPage()) { + // The same page means Bedrock is closing the window + ClientCloseWindowPacket closeWindowPacket = new ClientCloseWindowPacket(lecternContainer.getId()); + session.sendDownstreamPacket(closeWindowPacket); + InventoryUtils.closeInventory(session, lecternContainer.getId()); + } else { + // Each "page" Bedrock gives to us actually represents two pages (think opening a book and seeing two pages) + // Each "page" on Java is just one page (think a spiral notebook folded back to only show one page) + int newJavaPage = (packet.getPage() * 2); + int currentJavaPage = (lecternContainer.getCurrentBedrockPage() * 2); + // Send as many click button packets as we need to + // Java has the option to specify exact page numbers by adding 100 to the number, but buttonId variable + // is a byte and therefore this stops us at 128 + if (newJavaPage > currentJavaPage) { + for (int i = currentJavaPage; i < newJavaPage; i++) { + ClientClickWindowButtonPacket clickButtonPacket = new ClientClickWindowButtonPacket(session.getOpenInventory().getId(), 2); + System.out.println(clickButtonPacket); + session.sendDownstreamPacket(clickButtonPacket); + } + } else { + for (int i = currentJavaPage; i > newJavaPage; i--) { + ClientClickWindowButtonPacket clickButtonPacket = new ClientClickWindowButtonPacket(session.getOpenInventory().getId(), 1); + System.out.println(clickButtonPacket); + session.sendDownstreamPacket(clickButtonPacket); + } + } + } + } + } +} 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 95516dc2..396d1e09 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 @@ -97,6 +97,9 @@ public abstract class InventoryTranslator { /* Generics */ put(WindowType.GENERIC_3X3, new GenericBlockInventoryTranslator(9, "minecraft:dispenser[facing=north,triggered=false]", ContainerType.DISPENSER)); put(WindowType.HOPPER, new GenericBlockInventoryTranslator(5, "minecraft:hopper[enabled=false,facing=down]", ContainerType.HOPPER)); + + /* Lectern */ + put(WindowType.LECTERN, new LecternInventoryTranslator()); } }; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LecternInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LecternInventoryTranslator.java new file mode 100644 index 00000000..28562bb9 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LecternInventoryTranslator.java @@ -0,0 +1,159 @@ +/* + * 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.translators; + +import com.github.steveice10.mc.protocol.data.game.window.WindowType; +import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientClickWindowButtonPacket; +import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientCloseWindowPacket; +import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import com.github.steveice10.opennbt.tag.builtin.ListTag; +import com.nukkitx.math.vector.Vector3i; +import com.nukkitx.nbt.NbtMap; +import com.nukkitx.nbt.NbtMapBuilder; +import com.nukkitx.protocol.bedrock.data.inventory.ItemData; +import org.geysermc.connector.inventory.GeyserItemStack; +import org.geysermc.connector.inventory.Inventory; +import org.geysermc.connector.inventory.LecternContainer; +import org.geysermc.connector.inventory.PlayerInventory; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater; +import org.geysermc.connector.utils.BlockEntityUtils; +import org.geysermc.connector.utils.InventoryUtils; + +public class LecternInventoryTranslator extends BaseInventoryTranslator { + private final InventoryUpdater updater; + + public LecternInventoryTranslator() { + super(1); + this.updater = new LecternInventoryUpdater(); + } + + @Override + public void prepareInventory(GeyserSession session, Inventory inventory) { + + } + + @Override + public void openInventory(GeyserSession session, Inventory inventory) { + + } + + @Override + public void closeInventory(GeyserSession session, Inventory inventory) { + + } + + @Override + public void updateProperty(GeyserSession session, Inventory inventory, int key, int value) { + if (key == 0) { // Lectern page update + LecternContainer lecternContainer = (LecternContainer) inventory; + lecternContainer.setCurrentBedrockPage(value / 2); + lecternContainer.setBlockEntityTag(lecternContainer.getBlockEntityTag().toBuilder().putInt("page", lecternContainer.getCurrentBedrockPage()).build()); + System.out.println(lecternContainer.getBlockEntityTag()); + BlockEntityUtils.updateBlockEntity(session, lecternContainer.getBlockEntityTag(), lecternContainer.getPosition()); + } + } + + @Override + public void updateInventory(GeyserSession session, Inventory inventory) { + + } + + @Override + public void updateSlot(GeyserSession session, Inventory inventory, int slot) { + this.updater.updateSlot(this, session, inventory, slot); + if (slot == 0) { + LecternContainer lecternContainer = (LecternContainer) inventory; + if (session.isDroppingLecternBook()) { + // We have to enter the inventory GUI to eject the book + ClientClickWindowButtonPacket packet = new ClientClickWindowButtonPacket(inventory.getId(), 3); + session.sendDownstreamPacket(packet); + session.setDroppingLecternBook(false); + InventoryUtils.closeInventory(session, inventory.getId()); + } else if (lecternContainer.getBlockEntityTag() == null) { + // If the method returns true, this is already handled for us + GeyserItemStack geyserItemStack = inventory.getItem(0); + CompoundTag tag = geyserItemStack.getNbt(); + if (tag != null) { + // Position has to be the last interacted position... right? + Vector3i position = session.getLastInteractionPosition(); + // shouldRefresh means that we should boot out the + boolean shouldRefresh = !session.getConnector().getWorldManager().shouldExpectLecternHandled() && !session.getLecternCache().contains(position); + int pagesSize = ((ListTag) tag.get("pages")).size(); + ItemData itemData = geyserItemStack.getItemData(session); + NbtMapBuilder lecternTag = getBaseLecternTag(position.getX(), position.getY(), position.getZ(), pagesSize); + lecternTag.putCompound("book", NbtMap.builder() + .putByte("Count", (byte) itemData.getCount()) + .putShort("Damage", (short) 0) + .putString("Name", "minecraft:written_book") + .putCompound("tag", itemData.getTag()) + .build()); + lecternTag.putInt("page", lecternContainer.getCurrentBedrockPage()); + NbtMap blockEntityTag = lecternTag.build(); + // Even with serverside access to lecterns, we don't easily know which lectern this is, so we need to rebuild + // the block entity tag + lecternContainer.setBlockEntityTag(blockEntityTag); + System.out.println(blockEntityTag); + lecternContainer.setPosition(position); + if (shouldRefresh) { + // Update the lectern because it's not updated client-side + BlockEntityUtils.updateBlockEntity(session, blockEntityTag, position); + session.getLecternCache().add(position); + // Close the window - we will reopen it once the client has this data synced + ClientCloseWindowPacket closeWindowPacket = new ClientCloseWindowPacket(lecternContainer.getId()); + session.sendDownstreamPacket(closeWindowPacket); + InventoryUtils.closeInventory(session, inventory.getId()); + session.getConnector().getLogger().warning("Closing inventory"); + } + } + } + } + } + + @Override + public Inventory createInventory(String name, int windowId, WindowType windowType, PlayerInventory playerInventory) { + return new LecternContainer(name, windowId, this.size, playerInventory); + } + + public static NbtMapBuilder getBaseLecternTag(int x, int y, int z, int totalPages) { + NbtMapBuilder builder = NbtMap.builder() + .putInt("x", x) + .putInt("y", y) + .putInt("z", z) + .putString("id", "Lectern"); + if (totalPages != 0) { + builder.putByte("hasBook", (byte) 1); + builder.putInt("totalPages", totalPages); + } else { + builder.putByte("hasBook", (byte) 0); + } + return builder; + } + + private static class LecternInventoryUpdater extends InventoryUpdater { + + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaUnloadChunkTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaUnloadChunkTranslator.java index 1dba7286..a4a661cd 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaUnloadChunkTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaUnloadChunkTranslator.java @@ -44,10 +44,19 @@ public class JavaUnloadChunkTranslator extends PacketTranslator iterator = session.getSkullCache().keySet().iterator(); while (iterator.hasNext()) { Vector3i position = iterator.next(); - if (Math.floor(position.getX() / 16) == packet.getX() && Math.floor(position.getZ() / 16) == packet.getZ()) { + if (Math.floor((double) position.getX() / 16) == packet.getX() && Math.floor((double) position.getZ() / 16) == packet.getZ()) { session.getSkullCache().get(position).despawnEntity(session); iterator.remove(); } } + + // Do the same thing with lecterns + iterator = session.getLecternCache().iterator(); + while (iterator.hasNext()) { + Vector3i position = iterator.next(); + if (Math.floor((double) position.getX() / 16) == packet.getX() && Math.floor((double) position.getZ() / 16) == packet.getZ()) { + iterator.remove(); + } + } } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/GeyserWorldManager.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/GeyserWorldManager.java index 2ab3c010..6248a494 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/GeyserWorldManager.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/GeyserWorldManager.java @@ -30,10 +30,13 @@ import com.github.steveice10.mc.protocol.data.game.chunk.Column; import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; import com.github.steveice10.mc.protocol.data.game.setting.Difficulty; import com.github.steveice10.mc.protocol.packet.ingame.client.ClientChatPacket; +import com.nukkitx.nbt.NbtMap; +import com.nukkitx.nbt.NbtMapBuilder; import it.unimi.dsi.fastutil.objects.Object2ObjectMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.cache.ChunkCache; +import org.geysermc.connector.network.translators.inventory.translators.LecternInventoryTranslator; import org.geysermc.connector.utils.GameRule; public class GeyserWorldManager extends WorldManager { @@ -88,6 +91,29 @@ public class GeyserWorldManager extends WorldManager { return new int[1024]; } + @Override + public NbtMap getLecternDataAt(GeyserSession session, int x, int y, int z, boolean isChunkLoad) { + // Without direct server access, we can't get lectern information on-the-fly. + // I should have set this up so it's only called when there is a book in the block state. - Camotoy + NbtMapBuilder lecternTag = LecternInventoryTranslator.getBaseLecternTag(x, y, z, 1); + lecternTag.putCompound("book", NbtMap.builder() + .putByte("Count", (byte) 1) + .putShort("Damage", (short) 0) + .putString("Name", "minecraft:written_book") + .putCompound("tag", NbtMap.builder() + .putString("photoname", "") + .putString("text", "") + .build()) + .build()); + lecternTag.putInt("page", -1); // I'm surprisingly glad this exists - it forces Bedrock to stop reading immediately + return lecternTag.build(); + } + + @Override + public boolean shouldExpectLecternHandled() { + return false; + } + @Override public void setGameRule(GeyserSession session, String name, Object value) { session.sendDownstreamPacket(new ClientChatPacket("/gamerule " + name + " " + value)); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/WorldManager.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/WorldManager.java index aaafe2fe..b17af92a 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/WorldManager.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/WorldManager.java @@ -30,6 +30,7 @@ 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.setting.Difficulty; import com.nukkitx.math.vector.Vector3i; +import com.nukkitx.nbt.NbtMap; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.utils.GameRule; @@ -106,6 +107,32 @@ public abstract class WorldManager { */ public abstract int[] getBiomeDataAt(GeyserSession session, int x, int z); + /** + * Sigh.
+ * + * So, on Java Edition, the lectern is an inventory. Java opens it and gets the contents of the book there. + * On Bedrock, the lectern contents are part of the block entity tag. Therefore, Bedrock expects to have the contents + * of the lectern ready and present in the world. If the contents are not there, it takes at least two clicks for the + * lectern to update the tag and then present itself.
+ * + * We solve this problem by querying all loaded lecterns, where possible, and sending their information in a block entity + * tag. + * + * @param session the session of the player + * @param x the x coordinate of the lectern + * @param y the y coordinate of the lectern + * @param z the z coordinate of the lectern + * @param isChunkLoad if this is called during a chunk load or not. Changes behavior in certain instances. + * @return the Bedrock lectern block entity tag. This may not be the exact block entity tag - for example, Spigot's + * block handled must be done on the server thread, so we send the tag manually there. + */ + public abstract NbtMap getLecternDataAt(GeyserSession session, int x, int y, int z, boolean isChunkLoad); + + /** + * @return whether we should expect lectern data to update, or if we have to fall back on a workaround. + */ + public abstract boolean shouldExpectLecternHandled(); + /** * Updates a gamerule value on the Java server * diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockStateValues.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockStateValues.java index 2701f82f..3c8dd2c7 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockStateValues.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockStateValues.java @@ -42,6 +42,7 @@ public class BlockStateValues { private static final Int2ObjectMap DOUBLE_CHEST_VALUES = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap FLOWER_POT_VALUES = new Int2ObjectOpenHashMap<>(); private static final Map FLOWER_POT_BLOCKS = new HashMap<>(); + private static final Int2BooleanMap LECTERN_BOOK_STATES = new Int2BooleanOpenHashMap(); private static final Int2IntMap NOTEBLOCK_PITCHES = new Int2IntOpenHashMap(); private static final Int2BooleanMap IS_STICKY_PISTON = new Int2BooleanOpenHashMap(); private static final Int2BooleanMap PISTON_VALUES = new Int2BooleanOpenHashMap(); @@ -88,6 +89,11 @@ public class BlockStateValues { return; } + if (entry.getKey().startsWith("minecraft:lectern")) { + LECTERN_BOOK_STATES.put(javaBlockState, entry.getKey().contains("has_book=true")); + return; + } + JsonNode notePitch = entry.getValue().get("note_pitch"); if (notePitch != null) { NOTEBLOCK_PITCHES.put(javaBlockState, entry.getValue().get("note_pitch").intValue()); @@ -197,6 +203,10 @@ public class BlockStateValues { return FLOWER_POT_BLOCKS; } + public static Int2BooleanMap getLecternBookStates() { + return LECTERN_BOOK_STATES; + } + /** * The note that noteblocks output when hit is part of the block state in Java but sent as a BlockEventPacket in Bedrock. * This gives an integer pitch that Bedrock can use. diff --git a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java index 8950601a..0175450d 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java @@ -53,6 +53,7 @@ import org.geysermc.connector.entity.Entity; import org.geysermc.connector.entity.ItemFrameEntity; import org.geysermc.connector.entity.player.SkullPlayerEntity; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.inventory.translators.LecternInventoryTranslator; import org.geysermc.connector.network.translators.world.block.BlockStateValues; import org.geysermc.connector.network.translators.world.block.BlockTranslator; import org.geysermc.connector.network.translators.world.block.entity.BedrockOnlyBlockEntity; @@ -282,7 +283,6 @@ public class ChunkUtils { } String id = BlockEntityUtils.getBedrockBlockEntityId(tagName); - BlockEntityTranslator blockEntityTranslator = BlockEntityUtils.getBlockEntityTranslator(id); Position pos = new Position((int) tag.get("x").getValue(), (int) tag.get("y").getValue(), (int) tag.get("z").getValue()); // Get Java blockstate ID from block entity position @@ -292,6 +292,14 @@ public class ChunkUtils { blockState = section.get(pos.getX() & 0xF, pos.getY() & 0xF, pos.getZ() & 0xF); } + if (tagName.equals("minecraft:lectern") && BlockStateValues.getLecternBookStates().get(blockState)) { + // If getLecternBookStates is false, let's just treat it like a normal block entity + bedrockBlockEntities[i] = session.getConnector().getWorldManager().getLecternDataAt(session, pos.getX(), pos.getY(), pos.getZ(), true); + i++; + continue; + } + + BlockEntityTranslator blockEntityTranslator = BlockEntityUtils.getBlockEntityTranslator(id); bedrockBlockEntities[i] = blockEntityTranslator.getBlockEntityTag(tagName, tag, blockState); // Check for custom skulls @@ -388,6 +396,29 @@ public class ChunkUtils { } session.sendUpstreamPacket(waterPacket); + if (BlockStateValues.getLecternBookStates().containsKey(blockState)) { + boolean lecternCachedHasBook = session.getLecternCache().contains(position); + boolean newLecternHasBook = BlockStateValues.getLecternBookStates().get(blockState); + if (!session.getConnector().getWorldManager().shouldExpectLecternHandled() && lecternCachedHasBook != newLecternHasBook) { + // Refresh the block entirely - it either has a book or no longer has a book + session.getConnector().getLogger().warning("Refreshing lectern entirely"); + NbtMap newLecternTag; + if (newLecternHasBook) { + newLecternTag = session.getConnector().getWorldManager().getLecternDataAt(session, position.getX(), position.getY(), position.getZ(), false); + } else { + session.getLecternCache().remove(position); + newLecternTag = LecternInventoryTranslator.getBaseLecternTag(position.getX(), position.getY(), position.getZ(), 0).build(); + } + BlockEntityUtils.updateBlockEntity(session, newLecternTag, position); + } else { + // As of right now, no tag can be added asynchronously + session.getConnector().getWorldManager().getLecternDataAt(session, position.getX(), position.getY(), position.getZ(), false); + } + } else { + // Lectern has been destroyed, if it existed + session.getLecternCache().remove(position); + } + // Since Java stores bed colors/skull information as part of the namespaced ID and Bedrock stores it as a tag // This is the only place I could find that interacts with the Java block state and block updates // Iterates through all block entity translators and determines if the block state needs to be saved diff --git a/connector/src/main/java/org/geysermc/connector/utils/DimensionUtils.java b/connector/src/main/java/org/geysermc/connector/utils/DimensionUtils.java index de9bcf88..e0118b43 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/DimensionUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/DimensionUtils.java @@ -66,6 +66,7 @@ public class DimensionUtils { session.getEntityCache().removeAllEntities(); session.getItemFrameCache().clear(); + session.getLecternCache().clear(); session.getSkullCache().clear(); if (session.getPendingDimSwitches().getAndIncrement() > 0) { ChunkUtils.sendEmptyChunks(session, player.getPosition().toInt(), 3, true); From 3ba396e6259219e41092457d03d76a8773d67734 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Mon, 28 Dec 2020 00:47:10 -0500 Subject: [PATCH 26/39] Fix some temporary inventory blocks not disappearing --- .../translators/inventory/holder/BlockInventoryHolder.java | 1 + 1 file changed, 1 insertion(+) 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 index 1b4dfb8d..7785c4a8 100644 --- 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 @@ -96,6 +96,7 @@ public class BlockInventoryHolder extends InventoryHolder { blockPacket.setDataLayer(0); blockPacket.setBlockPosition(holderPos); blockPacket.setRuntimeId(BlockTranslator.getBedrockBlockId(realBlock)); + blockPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY); session.sendUpstreamPacket(blockPacket); } } From a5c020e7eeff3527f82700026278f8a20edafa01 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Mon, 28 Dec 2020 13:16:17 -0500 Subject: [PATCH 27/39] More comments --- .../inventory/translators/LecternInventoryTranslator.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LecternInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LecternInventoryTranslator.java index 28562bb9..0eabf951 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LecternInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LecternInventoryTranslator.java @@ -72,7 +72,6 @@ public class LecternInventoryTranslator extends BaseInventoryTranslator { LecternContainer lecternContainer = (LecternContainer) inventory; lecternContainer.setCurrentBedrockPage(value / 2); lecternContainer.setBlockEntityTag(lecternContainer.getBlockEntityTag().toBuilder().putInt("page", lecternContainer.getCurrentBedrockPage()).build()); - System.out.println(lecternContainer.getBlockEntityTag()); BlockEntityUtils.updateBlockEntity(session, lecternContainer.getBlockEntityTag(), lecternContainer.getPosition()); } } @@ -100,7 +99,7 @@ public class LecternInventoryTranslator extends BaseInventoryTranslator { if (tag != null) { // Position has to be the last interacted position... right? Vector3i position = session.getLastInteractionPosition(); - // shouldRefresh means that we should boot out the + // shouldRefresh means that we should boot out the client on our side because their lectern GUI isn't updated yet boolean shouldRefresh = !session.getConnector().getWorldManager().shouldExpectLecternHandled() && !session.getLecternCache().contains(position); int pagesSize = ((ListTag) tag.get("pages")).size(); ItemData itemData = geyserItemStack.getItemData(session); @@ -116,7 +115,6 @@ public class LecternInventoryTranslator extends BaseInventoryTranslator { // Even with serverside access to lecterns, we don't easily know which lectern this is, so we need to rebuild // the block entity tag lecternContainer.setBlockEntityTag(blockEntityTag); - System.out.println(blockEntityTag); lecternContainer.setPosition(position); if (shouldRefresh) { // Update the lectern because it's not updated client-side @@ -148,6 +146,7 @@ public class LecternInventoryTranslator extends BaseInventoryTranslator { builder.putByte("hasBook", (byte) 1); builder.putInt("totalPages", totalPages); } else { + // Not usually needed, but helps with kicking out Bedrock players from reading the UI builder.putByte("hasBook", (byte) 0); } return builder; From 3c1a40c56af6a708b48c8077a90379176b6f876d Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Tue, 29 Dec 2020 19:59:22 -0500 Subject: [PATCH 28/39] Better net ID handling --- .../org/geysermc/connector/entity/Entity.java | 6 +-- .../connector/inventory/Container.java | 7 ++-- .../connector/inventory/GeyserItemStack.java | 37 +++++++++++-------- .../connector/inventory/Inventory.java | 13 ++++++- ...BedrockInventoryTransactionTranslator.java | 2 +- .../entity/BedrockEntityEventTranslator.java | 2 +- .../player/BedrockInteractTranslator.java | 2 +- .../inventory/InventoryTranslator.java | 36 +++++++++--------- .../inventory/click/ClickPlan.java | 2 +- .../translators/LoomInventoryTranslator.java | 2 +- .../StonecutterInventoryTranslator.java | 6 +-- .../java/window/JavaSetSlotTranslator.java | 10 +---- .../window/JavaWindowItemsTranslator.java | 8 +--- .../connector/utils/InventoryUtils.java | 2 +- 14 files changed, 68 insertions(+), 67 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/entity/Entity.java b/connector/src/main/java/org/geysermc/connector/entity/Entity.java index 68b2b266..921d5e14 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/Entity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/Entity.java @@ -286,11 +286,11 @@ public class Entity { // Shield code if (session.getPlayerEntity().getEntityId() == entityId && metadata.getFlags().getFlag(EntityFlag.SNEAKING)) { PlayerInventory playerInv = session.getPlayerInventory(); - if ((playerInv.getItemInHand().getId() == ItemRegistry.SHIELD.getJavaId()) || - (playerInv.getOffhand().getId() == ItemRegistry.SHIELD.getJavaId())) { + if ((playerInv.getItemInHand().getJavaId() == ItemRegistry.SHIELD.getJavaId()) || + (playerInv.getOffhand().getJavaId() == ItemRegistry.SHIELD.getJavaId())) { ClientPlayerUseItemPacket useItemPacket; metadata.getFlags().setFlag(EntityFlag.BLOCKING, true); - if (playerInv.getItemInHand().getId() == ItemRegistry.SHIELD.getJavaId()) { + if (playerInv.getItemInHand().getJavaId() == ItemRegistry.SHIELD.getJavaId()) { useItemPacket = new ClientPlayerUseItemPacket(Hand.MAIN_HAND); } // Else we just assume it's the offhand, to simplify logic and to assure the packet gets sent diff --git a/connector/src/main/java/org/geysermc/connector/inventory/Container.java b/connector/src/main/java/org/geysermc/connector/inventory/Container.java index eb1d9c3d..9768520b 100644 --- a/connector/src/main/java/org/geysermc/connector/inventory/Container.java +++ b/connector/src/main/java/org/geysermc/connector/inventory/Container.java @@ -27,6 +27,7 @@ package org.geysermc.connector.inventory; import lombok.Getter; import lombok.NonNull; +import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.inventory.InventoryTranslator; /** @@ -53,11 +54,11 @@ public class Container extends Inventory { } @Override - public void setItem(int slot, @NonNull GeyserItemStack item) { + public void setItem(int slot, @NonNull GeyserItemStack newItem, GeyserSession session) { if (slot < this.size) { - super.setItem(slot, item); + super.setItem(slot, newItem, session); } else { - playerInventory.setItem(slot - this.size + InventoryTranslator.PLAYER_INVENTORY_OFFSET, item); + playerInventory.setItem(slot - this.size + InventoryTranslator.PLAYER_INVENTORY_OFFSET, newItem, session); } } diff --git a/connector/src/main/java/org/geysermc/connector/inventory/GeyserItemStack.java b/connector/src/main/java/org/geysermc/connector/inventory/GeyserItemStack.java index c935fcdb..46cf8529 100644 --- a/connector/src/main/java/org/geysermc/connector/inventory/GeyserItemStack.java +++ b/connector/src/main/java/org/geysermc/connector/inventory/GeyserItemStack.java @@ -39,32 +39,34 @@ import org.geysermc.connector.network.translators.item.ItemTranslator; public class GeyserItemStack { public static final GeyserItemStack EMPTY = new GeyserItemStack(0, 0, null); - private final int id; + private final int javaId; private int amount; private CompoundTag nbt; private int netId; + private boolean netIdWasUpdated; - public GeyserItemStack(int id) { - this(id, 1); + public GeyserItemStack(int javaId) { + this(javaId, 1); } - public GeyserItemStack(int id, int amount) { - this(id, amount, null); + public GeyserItemStack(int javaId, int amount) { + this(javaId, amount, null); } - public GeyserItemStack(int id, int amount, CompoundTag nbt) { - this(id, amount, nbt, 1); + public GeyserItemStack(int javaId, int amount, CompoundTag nbt) { + this(javaId, amount, nbt, 1); } - public GeyserItemStack(int id, int amount, CompoundTag nbt, int netId) { - this.id = id; + public GeyserItemStack(int javaId, int amount, CompoundTag nbt, int netId) { + this.javaId = javaId; this.amount = amount; this.nbt = nbt; this.netId = netId; + this.netIdWasUpdated = !this.isEmpty(); } - public int getId() { - return isEmpty() ? 0 : id; + public int getJavaId() { + return isEmpty() ? 0 : javaId; } public int getAmount() { @@ -75,6 +77,11 @@ public class GeyserItemStack { return isEmpty() ? null : nbt; } + public void setNetId(int netId) { + this.netId = netId; + this.netIdWasUpdated = true; + } + public int getNetId() { return isEmpty() ? 0 : netId; } @@ -96,7 +103,7 @@ public class GeyserItemStack { } public ItemStack getItemStack() { - return isEmpty() ? null : new ItemStack(id, amount, nbt); + return isEmpty() ? null : new ItemStack(javaId, amount, nbt); } public ItemData getItemData(GeyserSession session) { @@ -106,11 +113,11 @@ public class GeyserItemStack { } public ItemEntry getItemEntry() { - return ItemRegistry.ITEM_ENTRIES.get(getId()); + return ItemRegistry.ITEM_ENTRIES.get(getJavaId()); } public boolean isEmpty() { - return amount <= 0 || id == 0; + return amount <= 0 || javaId == 0; } public GeyserItemStack copy() { @@ -118,6 +125,6 @@ public class GeyserItemStack { } public GeyserItemStack copy(int newAmount) { - return isEmpty() ? EMPTY : new GeyserItemStack(id, newAmount, nbt == null ? null : nbt.clone(), netId); + return isEmpty() ? EMPTY : new GeyserItemStack(javaId, newAmount, nbt == null ? null : nbt.clone(), netId); } } diff --git a/connector/src/main/java/org/geysermc/connector/inventory/Inventory.java b/connector/src/main/java/org/geysermc/connector/inventory/Inventory.java index 14d80c38..53d0905c 100644 --- a/connector/src/main/java/org/geysermc/connector/inventory/Inventory.java +++ b/connector/src/main/java/org/geysermc/connector/inventory/Inventory.java @@ -29,6 +29,7 @@ import com.nukkitx.math.vector.Vector3i; import lombok.Getter; import lombok.NonNull; import lombok.Setter; +import org.geysermc.connector.network.session.GeyserSession; import java.util.Arrays; @@ -76,8 +77,16 @@ public class Inventory { return items[slot]; } - public void setItem(int slot, @NonNull GeyserItemStack item) { - items[slot] = item; + public void setItem(int slot, @NonNull GeyserItemStack newItem, GeyserSession session) { + GeyserItemStack oldItem = items[slot]; + if (!newItem.isEmpty()) { + if (newItem.getItemData(session).equals(oldItem.getItemData(session), false, false, false)) { + newItem.setNetId(oldItem.getNetId()); + } else { + newItem.setNetId(session.getNextItemNetId()); + } + } + items[slot] = newItem; } public short getNextTransactionId() { 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 54221b4a..9e71455c 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 @@ -186,7 +186,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator= 0 && packet.getData() < trades.length) { VillagerTrade trade = merchantInventory.getVillagerTrades()[packet.getData()]; - openInventory.setItem(2, GeyserItemStack.from(trade.getOutput())); + openInventory.setItem(2, GeyserItemStack.from(trade.getOutput()), session); villager.getMetadata().put(EntityData.TRADE_XP, trade.getXp() + villager.getMetadata().getInt(EntityData.TRADE_XP)); villager.updateBedrockMetadata(session); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockInteractTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockInteractTranslator.java index 86f30c2f..0c694fb9 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockInteractTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockInteractTranslator.java @@ -100,7 +100,7 @@ public class BedrockInteractTranslator extends PacketTranslator switch (packet.getAction()) { case INTERACT: - if (session.getPlayerInventory().getItemInHand().getId() == ItemRegistry.SHIELD.getJavaId()) { + if (session.getPlayerInventory().getItemInHand().getJavaId() == ItemRegistry.SHIELD.getJavaId()) { break; } ClientPlayerInteractEntityPacket interactPacket = new ClientPlayerInteractEntityPacket((int) entity.getEntityId(), 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 ef052409..7e626955 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 @@ -213,7 +213,7 @@ public abstract class InventoryTranslator { GeyserItemStack newItem = sourceItem.copy(); if (sourceIsCursor) { GeyserItemStack destItem = inventory.getItem(destSlot); - if (destItem.getId() == sourceItem.getId()) { + if (destItem.getJavaId() == sourceItem.getJavaId()) { // Combining items int itemsLeftOver = destItem.getAmount() + transferAction.getCount(); if (itemsLeftOver > MAX_ITEM_STACK_SIZE) { @@ -235,7 +235,7 @@ public abstract class InventoryTranslator { } } else { // Delete the source since we're moving it - inventory.setItem(sourceSlot, GeyserItemStack.EMPTY); + inventory.setItem(sourceSlot, GeyserItemStack.EMPTY, session); ClientCreativeInventoryActionPacket creativeActionPacket = new ClientCreativeInventoryActionPacket( sourceSlot, new ItemStack(0) @@ -252,13 +252,13 @@ public abstract class InventoryTranslator { if (sourceIsCursor) { session.getPlayerInventory().setCursor(GeyserItemStack.EMPTY); } else { - inventory.setItem(sourceSlot, GeyserItemStack.EMPTY); + inventory.setItem(sourceSlot, GeyserItemStack.EMPTY, session); } } if (destIsCursor) { session.getPlayerInventory().setCursor(newItem); } else { - inventory.setItem(destSlot, newItem); + inventory.setItem(destSlot, newItem, session); } GeyserItemStack itemToUpdate = destIsCursor ? sourceItem : newItem; // The Java server doesn't care about what's in the mouse in creative mode, so we just need to track @@ -386,7 +386,7 @@ public abstract class InventoryTranslator { ); System.out.println(creativeActionPacket); session.sendDownstreamPacket(creativeActionPacket); - inventory.setItem(sourceSlot, oldDestinationItem); + inventory.setItem(sourceSlot, oldDestinationItem, session); } if (isCursor(swapAction.getDestination())) { session.getPlayerInventory().setCursor(oldSourceItem); @@ -397,7 +397,7 @@ public abstract class InventoryTranslator { ); System.out.println(creativeActionPacket); session.sendDownstreamPacket(creativeActionPacket); - inventory.setItem(destSlot, oldSourceItem); + inventory.setItem(destSlot, oldSourceItem, session); } } else if (isCursor(swapAction.getSource()) && isCursor(swapAction.getDestination())) { //??? @@ -499,7 +499,7 @@ public abstract class InventoryTranslator { ); session.sendDownstreamPacket(destroyItemPacket); System.out.println(destroyItemPacket); - inventory.setItem(javaSlot, GeyserItemStack.EMPTY); + inventory.setItem(javaSlot, GeyserItemStack.EMPTY, session); affectedSlots.add(javaSlot); } else { // Just sync up the item on our end, since the server doesn't care what's in our cursor @@ -522,7 +522,7 @@ public abstract class InventoryTranslator { GeyserItemStack item = inventory.getItem(sourceSlot); item.setAmount(item.getAmount() - consumeData.getCount()); if (item.isEmpty()) { - inventory.setItem(sourceSlot, GeyserItemStack.EMPTY); + inventory.setItem(sourceSlot, GeyserItemStack.EMPTY, session); } affectedSlots.add(sourceSlot); } @@ -718,16 +718,16 @@ public abstract class InventoryTranslator { session.getPlayerInventory().setCursor(GeyserItemStack.from(javaCreativeItem, session.getNextItemNetId())); return acceptRequest(request, Collections.singletonList( new ItemStackResponsePacket.ContainerEntry(ContainerSlotType.CURSOR, - Collections.singletonList(makeItemEntry(session, 0, session.getPlayerInventory().getCursor()))))); + Collections.singletonList(makeItemEntry(0, session.getPlayerInventory().getCursor()))))); } else { int javaSlot = bedrockSlotToJava(transferAction.getDestination()); GeyserItemStack existingItem = inventory.getItem(javaSlot); - if (existingItem.getId() == javaCreativeItem.getId()) { + if (existingItem.getJavaId() == javaCreativeItem.getId()) { // Adding more to an existing item existingItem.setAmount(existingItem.getAmount() + transferAction.getCount()); javaCreativeItem = existingItem.getItemStack(); } else { - inventory.setItem(javaSlot, GeyserItemStack.from(javaCreativeItem, session.getNextItemNetId())); + inventory.setItem(javaSlot, GeyserItemStack.from(javaCreativeItem, session.getNextItemNetId()), session); } ClientCreativeInventoryActionPacket creativeActionPacket = new ClientCreativeInventoryActionPacket( javaSlot, @@ -765,8 +765,8 @@ public abstract class InventoryTranslator { public boolean checkNetId(GeyserSession session, Inventory inventory, StackRequestSlotInfoData slotInfoData) { if (slotInfoData.getStackNetworkId() < 0) return true; - if (slotInfoData.getContainer() == ContainerSlotType.CURSOR) //TODO: temporary - return true; +// if (slotInfoData.getContainer() == ContainerSlotType.CURSOR) //TODO: temporary +// return true; GeyserItemStack currentItem = isCursor(slotInfoData) ? session.getPlayerInventory().getCursor() : inventory.getItem(bedrockSlotToJava(slotInfoData)); return currentItem.getNetId() == slotInfoData.getStackNetworkId(); @@ -826,7 +826,7 @@ public abstract class InventoryTranslator { for (int slot : affectedSlots) { BedrockContainerSlot bedrockSlot = javaSlotToBedrockContainer(slot); List list = containerMap.computeIfAbsent(bedrockSlot.getContainer(), k -> new ArrayList<>()); - list.add(makeItemEntry(session, bedrockSlot.getSlot(), inventory.getItem(slot))); + list.add(makeItemEntry(bedrockSlot.getSlot(), inventory.getItem(slot))); } List containerEntries = new ArrayList<>(); @@ -834,18 +834,16 @@ public abstract class InventoryTranslator { containerEntries.add(new ItemStackResponsePacket.ContainerEntry(entry.getKey(), entry.getValue())); } - ItemStackResponsePacket.ItemEntry cursorEntry = makeItemEntry(session, 0, session.getPlayerInventory().getCursor()); + ItemStackResponsePacket.ItemEntry cursorEntry = makeItemEntry(0, session.getPlayerInventory().getCursor()); containerEntries.add(new ItemStackResponsePacket.ContainerEntry(ContainerSlotType.CURSOR, Collections.singletonList(cursorEntry))); return containerEntries; } - public static ItemStackResponsePacket.ItemEntry makeItemEntry(GeyserSession session, int bedrockSlot, GeyserItemStack itemStack) { + public static ItemStackResponsePacket.ItemEntry makeItemEntry(int bedrockSlot, GeyserItemStack itemStack) { ItemStackResponsePacket.ItemEntry itemEntry; if (!itemStack.isEmpty()) { - int newNetId = session.getNextItemNetId(); - itemStack.setNetId(newNetId); - itemEntry = new ItemStackResponsePacket.ItemEntry((byte) bedrockSlot, (byte) bedrockSlot, (byte) itemStack.getAmount(), newNetId, ""); + itemEntry = new ItemStackResponsePacket.ItemEntry((byte) bedrockSlot, (byte) bedrockSlot, (byte) itemStack.getAmount(), itemStack.getNetId(), ""); } else { itemEntry = new ItemStackResponsePacket.ItemEntry((byte) bedrockSlot, (byte) bedrockSlot, (byte) 0, 0, ""); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/ClickPlan.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/ClickPlan.java index 4e5c2855..5bfe197e 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/ClickPlan.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/ClickPlan.java @@ -127,7 +127,7 @@ public class ClickPlan { if (simulating) { simulatedItems.put(slot, item); } else { - inventory.setItem(slot, item); + inventory.setItem(slot, item, session); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LoomInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LoomInventoryTranslator.java index 59dd7d74..7fbca6e9 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LoomInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LoomInventoryTranslator.java @@ -166,7 +166,7 @@ public class LoomInventoryTranslator extends AbstractBlockInventoryTranslator { inputCopy.getNbt().put(blockEntityTag); } // Set the new item as the output - inventory.setItem(3, inputCopy); + inventory.setItem(3, inputCopy, session); return translateRequest(session, inventory, request); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/StonecutterInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/StonecutterInventoryTranslator.java index 16aefc74..4ffe87eb 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/StonecutterInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/StonecutterInventoryTranslator.java @@ -64,7 +64,7 @@ public class StonecutterInventoryTranslator extends AbstractBlockInventoryTransl } CraftResultsDeprecatedStackRequestActionData craftData = (CraftResultsDeprecatedStackRequestActionData) data; // Get the ID of the item we are cutting - int id = inventory.getItem(0).getId(); + int id = inventory.getItem(0).getJavaId(); // Look up all possible options of cutting from this ID IntList results = session.getStonecutterRecipes().get(id); if (results == null) { @@ -77,9 +77,9 @@ public class StonecutterInventoryTranslator extends AbstractBlockInventoryTransl ClientClickWindowButtonPacket packet = new ClientClickWindowButtonPacket(inventory.getId(), results.indexOf(javaOutput.getId())); System.out.println(packet.toString()); session.sendDownstreamPacket(packet); - if (inventory.getItem(1).getId() != javaOutput.getId()) { + if (inventory.getItem(1).getJavaId() != javaOutput.getId()) { // We don't know there is an output here, so we tell ourselves that there is - inventory.setItem(1, GeyserItemStack.from(javaOutput, session.getNextItemNetId())); + inventory.setItem(1, GeyserItemStack.from(javaOutput, session.getNextItemNetId()), session); } return translateRequest(session, inventory, request); } 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 5d311fe3..94c57271 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 @@ -62,15 +62,7 @@ public class JavaSetSlotTranslator extends PacketTranslator InventoryTranslator translator = session.getInventoryTranslator(); if (translator != null) { GeyserItemStack newItem = GeyserItemStack.from(packet.getItem()); - GeyserItemStack oldItem = inventory.getItem(packet.getSlot()); - if (newItem.getItemData(session).equals(oldItem.getItemData(session), false, false, false)) { - newItem.setNetId(oldItem.getNetId()); - System.out.println("OLD: " + newItem.getNetId()); - } else { - newItem.setNetId(session.getNextItemNetId()); - System.out.println("NEW: " + newItem.getNetId()); - } - inventory.setItem(packet.getSlot(), newItem); + inventory.setItem(packet.getSlot(), newItem, session); translator.updateSlot(session, inventory, packet.getSlot()); } }); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaWindowItemsTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaWindowItemsTranslator.java index b87f1de8..c8afd1c5 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaWindowItemsTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaWindowItemsTranslator.java @@ -46,13 +46,7 @@ public class JavaWindowItemsTranslator extends PacketTranslator Date: Tue, 29 Dec 2020 20:31:48 -0500 Subject: [PATCH 29/39] Optimize stonecutter button code --- .../connector/inventory/FurnaceInventory.java | 41 -------------- .../inventory/StonecutterContainer.java | 53 +++++++++++++++++++ .../StonecutterInventoryTranslator.java | 29 +++++++--- 3 files changed, 74 insertions(+), 49 deletions(-) delete mode 100644 connector/src/main/java/org/geysermc/connector/inventory/FurnaceInventory.java create mode 100644 connector/src/main/java/org/geysermc/connector/inventory/StonecutterContainer.java diff --git a/connector/src/main/java/org/geysermc/connector/inventory/FurnaceInventory.java b/connector/src/main/java/org/geysermc/connector/inventory/FurnaceInventory.java deleted file mode 100644 index 6574374b..00000000 --- a/connector/src/main/java/org/geysermc/connector/inventory/FurnaceInventory.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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.inventory; - -import lombok.Getter; -import lombok.Setter; - -//TODO: Figure out what this is and if we should remove it -@Getter -public class FurnaceInventory extends Inventory { - @Setter - private int test; - - public FurnaceInventory(String title, int id, int size) { - super(title, id, size); - } -} diff --git a/connector/src/main/java/org/geysermc/connector/inventory/StonecutterContainer.java b/connector/src/main/java/org/geysermc/connector/inventory/StonecutterContainer.java new file mode 100644 index 00000000..46db48b1 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/inventory/StonecutterContainer.java @@ -0,0 +1,53 @@ +/* + * 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.inventory; + +import lombok.Getter; +import lombok.NonNull; +import lombok.Setter; +import org.geysermc.connector.network.session.GeyserSession; + +public class StonecutterContainer extends Container { + /** + * The button that has currently been pressed Java-side + */ + @Getter + @Setter + private int stonecutterButton = -1; + + public StonecutterContainer(String title, int id, int size, PlayerInventory playerInventory) { + super(title, id, size, playerInventory); + } + + @Override + public void setItem(int slot, @NonNull GeyserItemStack newItem, GeyserSession session) { + if (slot == 0 && newItem.getJavaId() != items[slot].getJavaId()) { + // The pressed stonecutter button output resets whenever the input item changes + this.stonecutterButton = -1; + } + super.setItem(slot, newItem, session); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/StonecutterInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/StonecutterInventoryTranslator.java index 4ffe87eb..419e8060 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/StonecutterInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/StonecutterInventoryTranslator.java @@ -26,6 +26,7 @@ package org.geysermc.connector.network.translators.inventory.translators; import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; +import com.github.steveice10.mc.protocol.data.game.window.WindowType; import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientClickWindowButtonPacket; import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; @@ -38,6 +39,8 @@ import com.nukkitx.protocol.bedrock.packet.ItemStackResponsePacket; import it.unimi.dsi.fastutil.ints.IntList; import org.geysermc.connector.inventory.GeyserItemStack; import org.geysermc.connector.inventory.Inventory; +import org.geysermc.connector.inventory.PlayerInventory; +import org.geysermc.connector.inventory.StonecutterContainer; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.inventory.BedrockContainerSlot; import org.geysermc.connector.network.translators.inventory.SlotType; @@ -57,12 +60,12 @@ public class StonecutterInventoryTranslator extends AbstractBlockInventoryTransl @Override public ItemStackResponsePacket.Response translateSpecialRequest(GeyserSession session, Inventory inventory, ItemStackRequestPacket.Request request) { // TODO: Also surely to change in the future - // TODO: don't spam the ClickWindowButtonPacket? StackRequestActionData data = request.getActions()[1]; if (!(data instanceof CraftResultsDeprecatedStackRequestActionData)) { return rejectRequest(request); } CraftResultsDeprecatedStackRequestActionData craftData = (CraftResultsDeprecatedStackRequestActionData) data; + StonecutterContainer container = (StonecutterContainer) inventory; // Get the ID of the item we are cutting int id = inventory.getItem(0).getJavaId(); // Look up all possible options of cutting from this ID @@ -73,13 +76,18 @@ public class StonecutterInventoryTranslator extends AbstractBlockInventoryTransl System.out.println(id + " " + results); ItemStack javaOutput = ItemTranslator.translateToJava(craftData.getResultItems()[0]); System.out.println(javaOutput); - // Getting the index of the item in the Java stonecutter list - ClientClickWindowButtonPacket packet = new ClientClickWindowButtonPacket(inventory.getId(), results.indexOf(javaOutput.getId())); - System.out.println(packet.toString()); - session.sendDownstreamPacket(packet); - if (inventory.getItem(1).getJavaId() != javaOutput.getId()) { - // We don't know there is an output here, so we tell ourselves that there is - inventory.setItem(1, GeyserItemStack.from(javaOutput, session.getNextItemNetId()), session); + int button = results.indexOf(javaOutput.getId()); + // If we've already pressed the button with this item, no need to press it again! + if (container.getStonecutterButton() != button) { + // Getting the index of the item in the Java stonecutter list + ClientClickWindowButtonPacket packet = new ClientClickWindowButtonPacket(inventory.getId(), button); + System.out.println(packet.toString()); + session.sendDownstreamPacket(packet); + container.setStonecutterButton(button); + if (inventory.getItem(1).getJavaId() != javaOutput.getId()) { + // We don't know there is an output here, so we tell ourselves that there is + inventory.setItem(1, GeyserItemStack.from(javaOutput, session.getNextItemNetId()), session); + } } return translateRequest(session, inventory, request); } @@ -124,4 +132,9 @@ public class StonecutterInventoryTranslator extends AbstractBlockInventoryTransl } return super.getSlotType(javaSlot); } + + @Override + public Inventory createInventory(String name, int windowId, WindowType windowType, PlayerInventory playerInventory) { + return new StonecutterContainer(name, windowId, this.size, playerInventory); + } } From b7b3278d8bf3d19a6dd35b64a3fceb56f0c53fd0 Mon Sep 17 00:00:00 2001 From: AJ Ferguson Date: Thu, 31 Dec 2020 17:39:54 -0900 Subject: [PATCH 30/39] item painting. cursor net id --- .../connector/inventory/GeyserItemStack.java | 3 --- .../connector/inventory/Inventory.java | 6 +++++- .../connector/inventory/PlayerInventory.java | 7 ++++++- .../inventory/InventoryTranslator.java | 18 +++++++++--------- .../translators/inventory/click/ClickPlan.java | 18 +++++++++++++++--- .../JavaConfirmTransactionTranslator.java | 1 + .../java/window/JavaSetSlotTranslator.java | 8 +------- .../connector/utils/InventoryUtils.java | 2 +- 8 files changed, 38 insertions(+), 25 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/inventory/GeyserItemStack.java b/connector/src/main/java/org/geysermc/connector/inventory/GeyserItemStack.java index 46cf8529..c8443965 100644 --- a/connector/src/main/java/org/geysermc/connector/inventory/GeyserItemStack.java +++ b/connector/src/main/java/org/geysermc/connector/inventory/GeyserItemStack.java @@ -43,7 +43,6 @@ public class GeyserItemStack { private int amount; private CompoundTag nbt; private int netId; - private boolean netIdWasUpdated; public GeyserItemStack(int javaId) { this(javaId, 1); @@ -62,7 +61,6 @@ public class GeyserItemStack { this.amount = amount; this.nbt = nbt; this.netId = netId; - this.netIdWasUpdated = !this.isEmpty(); } public int getJavaId() { @@ -79,7 +77,6 @@ public class GeyserItemStack { public void setNetId(int netId) { this.netId = netId; - this.netIdWasUpdated = true; } public int getNetId() { diff --git a/connector/src/main/java/org/geysermc/connector/inventory/Inventory.java b/connector/src/main/java/org/geysermc/connector/inventory/Inventory.java index 53d0905c..1088f6f9 100644 --- a/connector/src/main/java/org/geysermc/connector/inventory/Inventory.java +++ b/connector/src/main/java/org/geysermc/connector/inventory/Inventory.java @@ -79,6 +79,11 @@ public class Inventory { public void setItem(int slot, @NonNull GeyserItemStack newItem, GeyserSession session) { GeyserItemStack oldItem = items[slot]; + updateItemNetId(oldItem, newItem, session); + items[slot] = newItem; + } + + protected static void updateItemNetId(GeyserItemStack oldItem, GeyserItemStack newItem, GeyserSession session) { if (!newItem.isEmpty()) { if (newItem.getItemData(session).equals(oldItem.getItemData(session), false, false, false)) { newItem.setNetId(oldItem.getNetId()); @@ -86,7 +91,6 @@ public class Inventory { newItem.setNetId(session.getNextItemNetId()); } } - items[slot] = newItem; } public short getNextTransactionId() { diff --git a/connector/src/main/java/org/geysermc/connector/inventory/PlayerInventory.java b/connector/src/main/java/org/geysermc/connector/inventory/PlayerInventory.java index 52066a80..b9f00b5f 100644 --- a/connector/src/main/java/org/geysermc/connector/inventory/PlayerInventory.java +++ b/connector/src/main/java/org/geysermc/connector/inventory/PlayerInventory.java @@ -28,6 +28,7 @@ package org.geysermc.connector.inventory; import lombok.Getter; import lombok.NonNull; import lombok.Setter; +import org.geysermc.connector.network.session.GeyserSession; public class PlayerInventory extends Inventory { @@ -40,7 +41,6 @@ public class PlayerInventory extends Inventory { private int heldItemSlot; @Getter - @Setter @NonNull private GeyserItemStack cursor = GeyserItemStack.EMPTY; @@ -49,6 +49,11 @@ public class PlayerInventory extends Inventory { heldItemSlot = 0; } + public void setCursor(@NonNull GeyserItemStack newCursor, GeyserSession session) { + updateItemNetId(cursor, newCursor, session); + cursor = newCursor; + } + public GeyserItemStack getItemInHand() { return items[36 + heldItemSlot]; } 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 7e626955..11ce68e4 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 @@ -223,7 +223,7 @@ public abstract class InventoryTranslator { } else { // Cursor will be emptied destItem.setAmount(itemsLeftOver); - session.getPlayerInventory().setCursor(GeyserItemStack.EMPTY); + session.getPlayerInventory().setCursor(GeyserItemStack.EMPTY, session); } ClientCreativeInventoryActionPacket creativeActionPacket = new ClientCreativeInventoryActionPacket( destSlot, @@ -250,13 +250,13 @@ public abstract class InventoryTranslator { if (sourceItem.isEmpty()) { // Item is basically deleted if (sourceIsCursor) { - session.getPlayerInventory().setCursor(GeyserItemStack.EMPTY); + session.getPlayerInventory().setCursor(GeyserItemStack.EMPTY, session); } else { inventory.setItem(sourceSlot, GeyserItemStack.EMPTY, session); } } if (destIsCursor) { - session.getPlayerInventory().setCursor(newItem); + session.getPlayerInventory().setCursor(newItem, session); } else { inventory.setItem(destSlot, newItem, session); } @@ -340,7 +340,7 @@ public abstract class InventoryTranslator { //try to transfer items with least clicks possible int halfSource = sourceAmount - (sourceAmount / 2); //larger half int holding; - if (transferAction.getCount() <= halfSource) { //faster to take only half + if (plan.getCursor().isEmpty() && transferAction.getCount() <= halfSource) { //faster to take only half. CURSOR MUST BE EMPTY plan.add(Click.RIGHT, sourceSlot); holding = halfSource; } else { //need all @@ -376,7 +376,7 @@ public abstract class InventoryTranslator { GeyserItemStack oldDestinationItem = inventory.getItem(destSlot); if (isCursor(swapAction.getSource())) { oldSourceItem = session.getPlayerInventory().getCursor(); - session.getPlayerInventory().setCursor(oldDestinationItem); + session.getPlayerInventory().setCursor(oldDestinationItem, session); } else { int sourceSlot = bedrockSlotToJava(swapAction.getSource()); oldSourceItem = inventory.getItem(sourceSlot); @@ -389,7 +389,7 @@ public abstract class InventoryTranslator { inventory.setItem(sourceSlot, oldDestinationItem, session); } if (isCursor(swapAction.getDestination())) { - session.getPlayerInventory().setCursor(oldSourceItem); + session.getPlayerInventory().setCursor(oldSourceItem, session); } else { ClientCreativeInventoryActionPacket creativeActionPacket = new ClientCreativeInventoryActionPacket( destSlot, @@ -445,7 +445,7 @@ public abstract class InventoryTranslator { cursorItem.setAmount(cursorItem.getAmount() - dropAction.getCount()); if (cursorItem.isEmpty()) { // Cursor item no longer exists - session.getPlayerInventory().setCursor(GeyserItemStack.EMPTY); + session.getPlayerInventory().setCursor(GeyserItemStack.EMPTY, session); } droppingItem.setAmount(dropAction.getCount()); ClientCreativeInventoryActionPacket packet = new ClientCreativeInventoryActionPacket( @@ -503,7 +503,7 @@ public abstract class InventoryTranslator { affectedSlots.add(javaSlot); } else { // Just sync up the item on our end, since the server doesn't care what's in our cursor - session.getPlayerInventory().setCursor(GeyserItemStack.EMPTY); + session.getPlayerInventory().setCursor(GeyserItemStack.EMPTY, session); } break; } @@ -715,7 +715,7 @@ public abstract class InventoryTranslator { ItemStack javaCreativeItem = ItemTranslator.translateToJava(creativeItem); if (isCursor(transferAction.getDestination())) { - session.getPlayerInventory().setCursor(GeyserItemStack.from(javaCreativeItem, session.getNextItemNetId())); + session.getPlayerInventory().setCursor(GeyserItemStack.from(javaCreativeItem, session.getNextItemNetId()), session); return acceptRequest(request, Collections.singletonList( new ItemStackResponsePacket.ContainerEntry(ContainerSlotType.CURSOR, Collections.singletonList(makeItemEntry(0, session.getPlayerInventory().getCursor()))))); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/ClickPlan.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/ClickPlan.java index 5bfe197e..64174c49 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/ClickPlan.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/ClickPlan.java @@ -63,6 +63,11 @@ public class ClickPlan { this.simulating = true; } + private void resetSimulation() { + this.simulatedItems.clear(); + this.simulatedCursor = session.getPlayerInventory().getCursor().copy(); + } + public void add(Click click, int slot) { if (!simulating) throw new UnsupportedOperationException("ClickPlan already executed"); @@ -77,7 +82,8 @@ public class ClickPlan { } public void execute(boolean refresh) { - simulating = false; + //update geyser inventory after simulation to avoid net id desync + resetSimulation(); ListIterator planIter = plan.listIterator(); while (planIter.hasNext()) { ClickAction action = planIter.next(); @@ -92,7 +98,7 @@ public class ClickPlan { } else if (action.click.windowAction == WindowAction.DROP_ITEM || action.slot == Click.OUTSIDE_SLOT) { clickedItemStack = null; } else { - clickedItemStack = inventory.getItem(action.slot).getItemStack(); + clickedItemStack = getItem(action.slot).getItemStack(); } short actionId = inventory.getNextTransactionId(); @@ -113,6 +119,12 @@ public class ClickPlan { } System.out.println(clickPacket); } + + session.getPlayerInventory().setCursor(simulatedCursor, session); + for (Int2ObjectMap.Entry simulatedSlot : simulatedItems.int2ObjectEntrySet()) { + inventory.setItem(simulatedSlot.getIntKey(), simulatedSlot.getValue(), session); + } + simulating = false; } public GeyserItemStack getItem(int slot) { @@ -135,7 +147,7 @@ public class ClickPlan { if (simulating) { simulatedCursor = item; } else { - session.getPlayerInventory().setCursor(item); + session.getPlayerInventory().setCursor(item, session); } } 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 6d6c1686..69e9b4ad 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 @@ -41,6 +41,7 @@ public class JavaConfirmTransactionTranslator extends PacketTranslator session.addInventoryTask(() -> { if (packet.getWindowId() == 255) { //cursor GeyserItemStack newItem = GeyserItemStack.from(packet.getItem()); - GeyserItemStack oldItem = session.getPlayerInventory().getCursor(); - if (newItem.getItemData(session).equals(oldItem.getItemData(session))) { - newItem.setNetId(oldItem.getNetId()); - } else { - newItem.setNetId(session.getNextItemNetId()); - } - session.getPlayerInventory().setCursor(newItem); + session.getPlayerInventory().setCursor(newItem, session); InventoryUtils.updateCursor(session); return; } 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 a15b5a1c..854654fb 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java @@ -79,7 +79,7 @@ public class InventoryUtils { } public static void closeInventory(GeyserSession session, int windowId) { - session.getPlayerInventory().setCursor(GeyserItemStack.EMPTY); + session.getPlayerInventory().setCursor(GeyserItemStack.EMPTY, session); updateCursor(session); Inventory inventory = getInventory(session, windowId); From 2a5c134ea7cc2d86bede701a7f53fa46dbd07029 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Fri, 1 Jan 2021 14:22:26 -0500 Subject: [PATCH 31/39] Update server inventory copyrights to 2021 --- .../connector/inventory/AnvilContainer.java | 2 +- .../inventory/CartographyContainer.java | 2 +- .../connector/inventory/Container.java | 2 +- .../inventory/EnchantingContainer.java | 2 +- .../inventory/GeyserEnchantOption.java | 2 +- .../connector/inventory/GeyserItemStack.java | 37 +++++++++---------- .../connector/inventory/LecternContainer.java | 2 +- .../inventory/MerchantContainer.java | 37 +++++++++---------- .../BedrockItemStackRequestTranslator.java | 37 +++++++++---------- .../BedrockLecternUpdateTranslator.java | 2 +- .../inventory/BedrockContainerSlot.java | 37 +++++++++---------- .../translators/inventory/click/Click.java | 2 +- .../inventory/click/ClickPlan.java | 2 +- .../translators/AnvilInventoryTranslator.java | 2 +- .../BeaconInventoryTranslator.java | 2 +- .../CartographyInventoryTranslator.java | 2 +- .../CraftingInventoryTranslator.java | 2 +- .../EnchantingInventoryTranslator.java | 2 +- .../GenericBlockInventoryTranslator.java | 2 +- .../LecternInventoryTranslator.java | 2 +- .../translators/LoomInventoryTranslator.java | 2 +- .../MerchantInventoryTranslator.java | 2 +- .../ShulkerInventoryTranslator.java | 2 +- .../StonecutterInventoryTranslator.java | 2 +- .../BlastFurnaceInventoryTranslator.java | 2 +- .../furnace/FurnaceInventoryTranslator.java | 2 +- .../furnace/SmokerInventoryTranslator.java | 2 +- .../AbstractHorseInventoryTranslator.java | 2 +- .../ChestedHorseInventoryTranslator.java | 2 +- .../horse/DonkeyInventoryTranslator.java | 2 +- .../horse/HorseInventoryTranslator.java | 2 +- .../horse/LlamaInventoryTranslator.java | 2 +- .../updater/HorseInventoryUpdater.java | 2 +- .../window/JavaOpenHorseWindowTranslator.java | 2 +- .../entity/BeaconBlockEntityTranslator.java | 2 +- 35 files changed, 103 insertions(+), 107 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/inventory/AnvilContainer.java b/connector/src/main/java/org/geysermc/connector/inventory/AnvilContainer.java index aba360a0..02e1c225 100644 --- a/connector/src/main/java/org/geysermc/connector/inventory/AnvilContainer.java +++ b/connector/src/main/java/org/geysermc/connector/inventory/AnvilContainer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2021 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/inventory/CartographyContainer.java b/connector/src/main/java/org/geysermc/connector/inventory/CartographyContainer.java index 7e6d1cc7..be4abd9e 100644 --- a/connector/src/main/java/org/geysermc/connector/inventory/CartographyContainer.java +++ b/connector/src/main/java/org/geysermc/connector/inventory/CartographyContainer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2021 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/inventory/Container.java b/connector/src/main/java/org/geysermc/connector/inventory/Container.java index 9768520b..520a76ef 100644 --- a/connector/src/main/java/org/geysermc/connector/inventory/Container.java +++ b/connector/src/main/java/org/geysermc/connector/inventory/Container.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2021 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/inventory/EnchantingContainer.java b/connector/src/main/java/org/geysermc/connector/inventory/EnchantingContainer.java index ab0e544d..8638e6ea 100644 --- a/connector/src/main/java/org/geysermc/connector/inventory/EnchantingContainer.java +++ b/connector/src/main/java/org/geysermc/connector/inventory/EnchantingContainer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2021 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/inventory/GeyserEnchantOption.java b/connector/src/main/java/org/geysermc/connector/inventory/GeyserEnchantOption.java index ae4a9cf4..a643fc19 100644 --- a/connector/src/main/java/org/geysermc/connector/inventory/GeyserEnchantOption.java +++ b/connector/src/main/java/org/geysermc/connector/inventory/GeyserEnchantOption.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2021 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/inventory/GeyserItemStack.java b/connector/src/main/java/org/geysermc/connector/inventory/GeyserItemStack.java index 46cf8529..a5cfb46c 100644 --- a/connector/src/main/java/org/geysermc/connector/inventory/GeyserItemStack.java +++ b/connector/src/main/java/org/geysermc/connector/inventory/GeyserItemStack.java @@ -1,27 +1,26 @@ /* - * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2021 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: + * 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 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 + * 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.inventory; diff --git a/connector/src/main/java/org/geysermc/connector/inventory/LecternContainer.java b/connector/src/main/java/org/geysermc/connector/inventory/LecternContainer.java index 0ce9217d..1b686a8f 100644 --- a/connector/src/main/java/org/geysermc/connector/inventory/LecternContainer.java +++ b/connector/src/main/java/org/geysermc/connector/inventory/LecternContainer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2021 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/inventory/MerchantContainer.java b/connector/src/main/java/org/geysermc/connector/inventory/MerchantContainer.java index a33f8147..f4f2d90e 100644 --- a/connector/src/main/java/org/geysermc/connector/inventory/MerchantContainer.java +++ b/connector/src/main/java/org/geysermc/connector/inventory/MerchantContainer.java @@ -1,27 +1,26 @@ /* - * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2021 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: + * 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 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 + * 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.inventory; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockItemStackRequestTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockItemStackRequestTranslator.java index 65083dde..03aaf3ba 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockItemStackRequestTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockItemStackRequestTranslator.java @@ -1,27 +1,26 @@ /* - * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2021 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: + * 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 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 + * 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.bedrock; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockLecternUpdateTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockLecternUpdateTranslator.java index 1ba08646..832d1347 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockLecternUpdateTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockLecternUpdateTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2021 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/BedrockContainerSlot.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/BedrockContainerSlot.java index b3a09e16..47d1f070 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/BedrockContainerSlot.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/BedrockContainerSlot.java @@ -1,27 +1,26 @@ /* - * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2021 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: + * 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 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 + * 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; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/Click.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/Click.java index d27290bf..fe4ac8bf 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/Click.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/Click.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2021 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/click/ClickPlan.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/ClickPlan.java index 5bfe197e..e2d554e2 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/ClickPlan.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/ClickPlan.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2021 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/translators/AnvilInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/AnvilInventoryTranslator.java index d4684057..b131544b 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/AnvilInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/AnvilInventoryTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2021 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/translators/BeaconInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/BeaconInventoryTranslator.java index 6407b9b3..5b31c83b 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/BeaconInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/BeaconInventoryTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2021 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/translators/CartographyInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/CartographyInventoryTranslator.java index 9c4eee1f..04794433 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/CartographyInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/CartographyInventoryTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2021 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/translators/CraftingInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/CraftingInventoryTranslator.java index 77eeee17..359a4559 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/CraftingInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/CraftingInventoryTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2021 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/translators/EnchantingInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/EnchantingInventoryTranslator.java index 50e8ec6a..4d105d42 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/EnchantingInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/EnchantingInventoryTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2021 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/translators/GenericBlockInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/GenericBlockInventoryTranslator.java index b54278bc..55df41c1 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/GenericBlockInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/GenericBlockInventoryTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2021 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/translators/LecternInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LecternInventoryTranslator.java index 0eabf951..9ce0ee0d 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LecternInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LecternInventoryTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2021 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/translators/LoomInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LoomInventoryTranslator.java index 7fbca6e9..bac9e7ae 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LoomInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LoomInventoryTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2021 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/translators/MerchantInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/MerchantInventoryTranslator.java index c19a81be..7422c7e4 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/MerchantInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/MerchantInventoryTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2021 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/translators/ShulkerInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/ShulkerInventoryTranslator.java index a0c4383e..43584df4 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/ShulkerInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/ShulkerInventoryTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2021 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/translators/StonecutterInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/StonecutterInventoryTranslator.java index 419e8060..fe21969d 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/StonecutterInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/StonecutterInventoryTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2021 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/translators/furnace/BlastFurnaceInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/furnace/BlastFurnaceInventoryTranslator.java index b7834dde..ed9a8a79 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/furnace/BlastFurnaceInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/furnace/BlastFurnaceInventoryTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2021 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/translators/furnace/FurnaceInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/furnace/FurnaceInventoryTranslator.java index f3d75941..b41c9b03 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/furnace/FurnaceInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/furnace/FurnaceInventoryTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2021 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/translators/furnace/SmokerInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/furnace/SmokerInventoryTranslator.java index 75eb33d9..2b9a78c7 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/furnace/SmokerInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/furnace/SmokerInventoryTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2021 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/translators/horse/AbstractHorseInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/horse/AbstractHorseInventoryTranslator.java index b33542d3..6c6c9a0c 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/horse/AbstractHorseInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/horse/AbstractHorseInventoryTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2021 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/translators/horse/ChestedHorseInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/horse/ChestedHorseInventoryTranslator.java index 3031e45a..f74c2d36 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/horse/ChestedHorseInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/horse/ChestedHorseInventoryTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2021 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/translators/horse/DonkeyInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/horse/DonkeyInventoryTranslator.java index 61a8d692..bf13bd6d 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/horse/DonkeyInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/horse/DonkeyInventoryTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2021 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/translators/horse/HorseInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/horse/HorseInventoryTranslator.java index 957933a0..09a8f5de 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/horse/HorseInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/horse/HorseInventoryTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2021 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/translators/horse/LlamaInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/horse/LlamaInventoryTranslator.java index 523a1d3e..cea605f8 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/horse/LlamaInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/horse/LlamaInventoryTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2021 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/updater/HorseInventoryUpdater.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/HorseInventoryUpdater.java index 2f88c49c..838e59d7 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/HorseInventoryUpdater.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/HorseInventoryUpdater.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2021 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/JavaOpenHorseWindowTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaOpenHorseWindowTranslator.java index 3d205d2a..e5748616 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaOpenHorseWindowTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaOpenHorseWindowTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2021 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/world/block/entity/BeaconBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BeaconBlockEntityTranslator.java index f60b0daa..147651a1 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BeaconBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BeaconBlockEntityTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2021 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 From 528a9a4431bd994f83d880297c823dc330f4e7d4 Mon Sep 17 00:00:00 2001 From: AJ Ferguson Date: Fri, 1 Jan 2021 18:37:33 -0900 Subject: [PATCH 32/39] Crafting table slot mappings --- .../translators/CraftingInventoryTranslator.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/CraftingInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/CraftingInventoryTranslator.java index 359a4559..81769c00 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/CraftingInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/CraftingInventoryTranslator.java @@ -67,4 +67,12 @@ public class CraftingInventoryTranslator extends AbstractBlockInventoryTranslato } return super.bedrockSlotToJava(slotInfoData); } + + @Override + public int javaSlotToBedrock(int slot) { + if (slot < size) { + return slot == 0 ? 50 : slot + 31; + } + return super.javaSlotToBedrock(slot); + } } From 8928d554a1a8dde5e4d826afbb809b57358b409f Mon Sep 17 00:00:00 2001 From: AJ Ferguson Date: Sun, 3 Jan 2021 17:54:26 -0900 Subject: [PATCH 33/39] WIP autocrafting using java recipe book work in progress. many edge cases are currently unhandled. will not work at all pre 1.12. (support is planned) --- .../network/session/GeyserSession.java | 3 + .../inventory/InventoryTranslator.java | 86 ++++++++++++++----- .../translators/inventory/click/Click.java | 6 +- .../inventory/click/ClickPlan.java | 16 +++- .../java/JavaDeclareRecipesTranslator.java | 1 + .../java/JavaUnlockRecipesTranslator.java | 48 +++++++++++ 6 files changed, 130 insertions(+), 30 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/java/JavaUnlockRecipesTranslator.java diff --git a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java index 10a58360..e2587ee0 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java @@ -61,6 +61,7 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2LongMap; import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectIterator; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import lombok.AccessLevel; import lombok.Getter; import lombok.NonNull; @@ -230,6 +231,7 @@ public class GeyserSession implements CommandSender { @Setter private Int2ObjectMap craftingRecipes; + private final Set unlockedRecipes; /** * Saves a list of all stonecutter recipes, for use in a stonecutter inventory. @@ -382,6 +384,7 @@ public class GeyserSession implements CommandSender { this.openInventory = null; this.inventoryFuture = CompletableFuture.completedFuture(null); this.craftingRecipes = new Int2ObjectOpenHashMap<>(); + this.unlockedRecipes = new ObjectOpenHashSet<>(); this.spawned = false; this.loggedIn = false; 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 ffc8d02f..bf2fb36c 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 @@ -27,8 +27,12 @@ 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.data.game.recipe.Recipe; +import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapedRecipeData; +import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapelessRecipeData; import com.github.steveice10.mc.protocol.data.game.window.WindowType; import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientCreativeInventoryActionPacket; +import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientPrepareCraftingGridPacket; import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; @@ -552,7 +556,8 @@ public abstract class InventoryTranslator { int recipeId = 0; int resultSize = 0; - boolean autoCraft; + int timesCrafted = 0; + boolean autoCraft = false; CraftState craftState = CraftState.START; int leftover = 0; @@ -566,28 +571,30 @@ public abstract class InventoryTranslator { } craftState = CraftState.RECIPE_ID; recipeId = craftAction.getRecipeNetworkId(); - //System.out.println(session.getCraftingRecipes().get(recipeId)); autoCraft = false; break; } -// case CRAFT_RECIPE_AUTO: { -// AutoCraftRecipeStackRequestActionData autoCraftAction = (AutoCraftRecipeStackRequestActionData) action; -// if (craftState != CraftState.START) { -// return rejectRequest(request); -// } -// craftState = CraftState.RECIPE_ID; -// recipeId = autoCraftAction.getRecipeNetworkId(); -// Recipe recipe = session.getCraftingRecipes().get(recipeId); -// System.out.println(recipe); -// if (recipe == null) { -// return rejectRequest(request); -// } -//// ClientPrepareCraftingGridPacket packet = new ClientPrepareCraftingGridPacket(session.getOpenInventory().getId(), recipe.getIdentifier(), true); -//// session.sendDownstreamPacket(packet); -// autoCraft = true; -// //TODO: reject transaction if crafting grid is not clear -// break; -// } + case CRAFT_RECIPE_AUTO: { + AutoCraftRecipeStackRequestActionData autoCraftAction = (AutoCraftRecipeStackRequestActionData) action; + if (craftState != CraftState.START) { + return rejectRequest(request); + } + craftState = CraftState.RECIPE_ID; + + recipeId = autoCraftAction.getRecipeNetworkId(); + if (!plan.getCursor().isEmpty()) { + return rejectRequest(request); + } + //reject if crafting grid is not clear + int gridSize = inventory.getId() == 0 ? 4 : 9; + for (int i = 1; i <= gridSize; i++) { + if (!inventory.getItem(i).isEmpty()) { + return rejectRequest(request); + } + } + autoCraft = true; + break; + } case CRAFT_RESULTS_DEPRECATED: { CraftResultsDeprecatedStackRequestActionData deprecatedCraftAction = (CraftResultsDeprecatedStackRequestActionData) action; if (craftState != CraftState.RECIPE_ID) { @@ -599,7 +606,8 @@ public abstract class InventoryTranslator { return rejectRequest(request); } resultSize = deprecatedCraftAction.getResultItems()[0].getCount(); - if (resultSize <= 0) { + timesCrafted = deprecatedCraftAction.getTimesCrafted(); + if (resultSize <= 0 || timesCrafted <= 0) { return rejectRequest(request); } break; @@ -628,11 +636,45 @@ public abstract class InventoryTranslator { } int sourceSlot = bedrockSlotToJava(transferAction.getSource()); + int destSlot = bedrockSlotToJava(transferAction.getDestination()); + + if (autoCraft) { + Recipe recipe = session.getCraftingRecipes().get(recipeId); + //cannot use java recipe book if recipe is locked + if (recipe == null || !session.getUnlockedRecipes().contains(recipe.getIdentifier())) { + return rejectRequest(request); + } + + boolean cursorDest = isCursor(transferAction.getDestination()); + boolean makeAll = timesCrafted > 1; + if (cursorDest) { + makeAll = false; + } + + ClientPrepareCraftingGridPacket prepareCraftingPacket = new ClientPrepareCraftingGridPacket(inventory.getId(), recipe.getIdentifier(), makeAll); + session.sendDownstreamPacket(prepareCraftingPacket); + + ItemStack output = null; + switch (recipe.getType()) { + case CRAFTING_SHAPED: + output = ((ShapedRecipeData)recipe.getData()).getResult(); + break; + case CRAFTING_SHAPELESS: + output = ((ShapelessRecipeData)recipe.getData()).getResult(); + break; + } + inventory.setItem(0, GeyserItemStack.from(output), session); + + plan.add(cursorDest ? Click.LEFT : Click.LEFT_SHIFT, 0); + plan.execute(true); + + return acceptRequest(request, makeContainerEntries(session, inventory, Collections.emptySet())); + } + if (isCursor(transferAction.getDestination())) { plan.add(Click.LEFT, sourceSlot); craftState = CraftState.DONE; } else { - int destSlot = bedrockSlotToJava(transferAction.getDestination()); if (leftover != 0) { if (transferAction.getCount() > leftover) { return rejectRequest(request); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/Click.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/Click.java index fe4ac8bf..d3666a9e 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/Click.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/Click.java @@ -25,16 +25,14 @@ package org.geysermc.connector.network.translators.inventory.click; -import com.github.steveice10.mc.protocol.data.game.window.ClickItemParam; -import com.github.steveice10.mc.protocol.data.game.window.DropItemParam; -import com.github.steveice10.mc.protocol.data.game.window.WindowAction; -import com.github.steveice10.mc.protocol.data.game.window.WindowActionParam; +import com.github.steveice10.mc.protocol.data.game.window.*; import lombok.AllArgsConstructor; @AllArgsConstructor public enum Click { LEFT(WindowAction.CLICK_ITEM, ClickItemParam.LEFT_CLICK), RIGHT(WindowAction.CLICK_ITEM, ClickItemParam.RIGHT_CLICK), + LEFT_SHIFT(WindowAction.SHIFT_CLICK_ITEM, ShiftClickItemParam.LEFT_CLICK), DROP_ONE(WindowAction.DROP_ITEM, DropItemParam.DROP_FROM_SELECTED), DROP_ALL(WindowAction.DROP_ITEM, DropItemParam.DROP_SELECTED_STACK), LEFT_OUTSIDE(WindowAction.CLICK_ITEM, ClickItemParam.LEFT_CLICK), diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/ClickPlan.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/ClickPlan.java index 830a9ce0..9268cbf1 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/ClickPlan.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/click/ClickPlan.java @@ -166,10 +166,15 @@ public class ClickPlan { GeyserItemStack clicked = simulating ? getItem(action.slot) : inventory.getItem(action.slot); if (translator.getSlotType(action.slot) == SlotType.OUTPUT) { - if (cursor.isEmpty() && !clicked.isEmpty()) { - setCursor(clicked.copy()); - } else if (InventoryUtils.canStack(cursor, clicked)) { - cursor.add(clicked.getAmount()); + switch (action.click) { + case LEFT: + case RIGHT: + if (cursor.isEmpty() && !clicked.isEmpty()) { + setCursor(clicked.copy()); + } else if (InventoryUtils.canStack(cursor, clicked)) { + cursor.add(clicked.getAmount()); + } + break; } } else { switch (action.click) { @@ -195,6 +200,9 @@ public class ClickPlan { clicked.add(1); } break; + case LEFT_SHIFT: + //TODO + break; case DROP_ONE: if (!clicked.isEmpty()) { clicked.sub(1); 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 ed0abe10..31726cc5 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 @@ -183,6 +183,7 @@ public class JavaDeclareRecipesTranslator extends PacketTranslator { + + @Override + public void translate(ServerUnlockRecipesPacket packet, GeyserSession session) { + if (packet.getAction() == UnlockRecipesAction.REMOVE) { + session.getUnlockedRecipes().removeAll(Arrays.asList(packet.getRecipes())); + } else { + session.getUnlockedRecipes().addAll(Arrays.asList(packet.getRecipes())); + } + } +} + From 50f295b4cd6a441a0ebd46cd0f83de72b390e9ba Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Mon, 4 Jan 2021 18:19:43 -0500 Subject: [PATCH 34/39] Remove unneeded GeyserItemStack constructor and add comments --- .../org/geysermc/connector/inventory/GeyserItemStack.java | 6 +----- .../network/translators/inventory/InventoryTranslator.java | 4 ++-- .../translators/StonecutterInventoryTranslator.java | 2 +- .../translators/java/JavaDeclareRecipesTranslator.java | 5 +++++ .../translators/java/JavaUnlockRecipesTranslator.java | 3 +++ 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/inventory/GeyserItemStack.java b/connector/src/main/java/org/geysermc/connector/inventory/GeyserItemStack.java index 6c6cf911..76f7674a 100644 --- a/connector/src/main/java/org/geysermc/connector/inventory/GeyserItemStack.java +++ b/connector/src/main/java/org/geysermc/connector/inventory/GeyserItemStack.java @@ -91,11 +91,7 @@ public class GeyserItemStack { } public static GeyserItemStack from(ItemStack itemStack) { - return from(itemStack, 1); - } - - public static GeyserItemStack from(ItemStack itemStack, int netId) { - return itemStack == null ? EMPTY : new GeyserItemStack(itemStack.getId(), itemStack.getAmount(), itemStack.getNbt(), netId); + return itemStack == null ? EMPTY : new GeyserItemStack(itemStack.getId(), itemStack.getAmount(), itemStack.getNbt()); } public ItemStack getItemStack() { 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 bf2fb36c..f0f734fc 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 @@ -757,7 +757,7 @@ public abstract class InventoryTranslator { ItemStack javaCreativeItem = ItemTranslator.translateToJava(creativeItem); if (isCursor(transferAction.getDestination())) { - session.getPlayerInventory().setCursor(GeyserItemStack.from(javaCreativeItem, session.getNextItemNetId()), session); + session.getPlayerInventory().setCursor(GeyserItemStack.from(javaCreativeItem), session); return acceptRequest(request, Collections.singletonList( new ItemStackResponsePacket.ContainerEntry(ContainerSlotType.CURSOR, Collections.singletonList(makeItemEntry(0, session.getPlayerInventory().getCursor()))))); @@ -769,7 +769,7 @@ public abstract class InventoryTranslator { existingItem.setAmount(existingItem.getAmount() + transferAction.getCount()); javaCreativeItem = existingItem.getItemStack(); } else { - inventory.setItem(javaSlot, GeyserItemStack.from(javaCreativeItem, session.getNextItemNetId()), session); + inventory.setItem(javaSlot, GeyserItemStack.from(javaCreativeItem), session); } ClientCreativeInventoryActionPacket creativeActionPacket = new ClientCreativeInventoryActionPacket( javaSlot, diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/StonecutterInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/StonecutterInventoryTranslator.java index fe21969d..0168d0ef 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/StonecutterInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/StonecutterInventoryTranslator.java @@ -86,7 +86,7 @@ public class StonecutterInventoryTranslator extends AbstractBlockInventoryTransl container.setStonecutterButton(button); if (inventory.getItem(1).getJavaId() != javaOutput.getId()) { // We don't know there is an output here, so we tell ourselves that there is - inventory.setItem(1, GeyserItemStack.from(javaOutput, session.getNextItemNetId()), session); + inventory.setItem(1, GeyserItemStack.from(javaOutput), session); } } return translateRequest(session, inventory, request); 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 31726cc5..2565c6ab 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 @@ -47,6 +47,11 @@ import org.geysermc.connector.network.translators.item.*; import java.util.*; import java.util.stream.Collectors; +/** + * Used to send all valid recipes from Java to Bedrock. + * + * Bedrock REQUIRES a CraftingDataPacket to be sent in order to craft anything. + */ @Translator(packet = ServerDeclareRecipesPacket.class) public class JavaDeclareRecipesTranslator extends PacketTranslator { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaUnlockRecipesTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaUnlockRecipesTranslator.java index 6b6aefc3..0a0ba4d2 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaUnlockRecipesTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaUnlockRecipesTranslator.java @@ -33,6 +33,9 @@ import org.geysermc.connector.network.translators.Translator; import java.util.Arrays; +/** + * Used to list recipes that we can definitely use the recipe book for (and therefore save on packet usage) + */ @Translator(packet = ServerUnlockRecipesPacket.class) public class JavaUnlockRecipesTranslator extends PacketTranslator { From a160e3694b2574c8214fac796f80d84672fd600d Mon Sep 17 00:00:00 2001 From: AJ Ferguson Date: Mon, 4 Jan 2021 14:47:48 -0900 Subject: [PATCH 35/39] Add stackSize to ItemEntry --- .../network/translators/item/ItemEntry.java | 3 ++- .../network/translators/item/ItemRegistry.java | 12 ++++++++---- .../network/translators/item/ToolItemEntry.java | 4 ++-- connector/src/main/resources/mappings | 2 +- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemEntry.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemEntry.java index f61c3d70..278d708f 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemEntry.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemEntry.java @@ -34,7 +34,7 @@ import lombok.ToString; @ToString public class ItemEntry { - public static ItemEntry AIR = new ItemEntry("minecraft:air", "minecraft:air", 0, 0, 0, false); + public static ItemEntry AIR = new ItemEntry("minecraft:air", "minecraft:air", 0, 0, 0, false, 64); private final String javaIdentifier; private final String bedrockIdentifier; @@ -43,6 +43,7 @@ public class ItemEntry { private final int bedrockData; private final boolean block; + private final int stackSize; @Override public boolean equals(Object obj) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java index 02cd839a..c2b60c8f 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java @@ -147,6 +147,7 @@ public class ItemRegistry { if (bedrockIdentifier == null) { throw new RuntimeException("Missing Bedrock ID in mappings!: " + bedrockId); } + int stackSize = entry.getValue().get("stack_size") == null ? 64 : entry.getValue().get("stack_size").intValue(); if (entry.getValue().has("tool_type")) { if (entry.getValue().has("tool_tier")) { ITEM_ENTRIES.put(itemIndex, new ToolItemEntry( @@ -154,19 +155,22 @@ public class ItemRegistry { entry.getValue().get("bedrock_data").intValue(), entry.getValue().get("tool_type").textValue(), entry.getValue().get("tool_tier").textValue(), - entry.getValue().get("is_block") != null && entry.getValue().get("is_block").booleanValue())); + entry.getValue().get("is_block") != null && entry.getValue().get("is_block").booleanValue(), + stackSize)); } else { ITEM_ENTRIES.put(itemIndex, new ToolItemEntry( entry.getKey(), bedrockIdentifier, itemIndex, bedrockId, entry.getValue().get("bedrock_data").intValue(), entry.getValue().get("tool_type").textValue(), - "", entry.getValue().get("is_block").booleanValue())); + "", entry.getValue().get("is_block").booleanValue(), + stackSize)); } } else { ITEM_ENTRIES.put(itemIndex, new ItemEntry( entry.getKey(), bedrockIdentifier, itemIndex, bedrockId, entry.getValue().get("bedrock_data").intValue(), - entry.getValue().get("is_block") != null && entry.getValue().get("is_block").booleanValue())); + entry.getValue().get("is_block") != null && entry.getValue().get("is_block").booleanValue(), + stackSize)); } switch (entry.getKey()) { case "minecraft:barrier": @@ -209,7 +213,7 @@ public class ItemRegistry { // Add the loadstone compass since it doesn't exist on java but we need it for item conversion ITEM_ENTRIES.put(itemIndex, new ItemEntry("minecraft:lodestone_compass", "minecraft:lodestone_compass", itemIndex, - lodestoneCompassId, 0, false)); + lodestoneCompassId, 0, false, 1)); /* Load creative items */ stream = FileUtils.getResource("bedrock/creative_items.json"); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ToolItemEntry.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ToolItemEntry.java index 5352938c..ba1753a3 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/ToolItemEntry.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/ToolItemEntry.java @@ -32,8 +32,8 @@ public class ToolItemEntry extends ItemEntry { private final String toolType; private final String toolTier; - public ToolItemEntry(String javaIdentifier, String bedrockIdentifier, int javaId, int bedrockId, int bedrockData, String toolType, String toolTier, boolean isBlock) { - super(javaIdentifier, bedrockIdentifier, javaId, bedrockId, bedrockData, isBlock); + public ToolItemEntry(String javaIdentifier, String bedrockIdentifier, int javaId, int bedrockId, int bedrockData, String toolType, String toolTier, boolean isBlock, int stackSize) { + super(javaIdentifier, bedrockIdentifier, javaId, bedrockId, bedrockData, isBlock, stackSize); this.toolType = toolType; this.toolTier = toolTier; } diff --git a/connector/src/main/resources/mappings b/connector/src/main/resources/mappings index 143285af..62e39aca 160000 --- a/connector/src/main/resources/mappings +++ b/connector/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit 143285afb4bdf4d5ef40ef7a7959477dabf4d34c +Subproject commit 62e39acaf3859da86189a76fff57b30c2403ef3d From 57e176efd6572f56fc8fe3921fd32aa8b255d9f0 Mon Sep 17 00:00:00 2001 From: AJ Ferguson Date: Mon, 4 Jan 2021 15:12:26 -0900 Subject: [PATCH 36/39] Optimize stackSize Co-Authored-By: Camotoy <20743703+Camotoy@users.noreply.github.com> --- .../connector/network/translators/item/ItemRegistry.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java index c2b60c8f..adb91aa3 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java @@ -147,7 +147,8 @@ public class ItemRegistry { if (bedrockIdentifier == null) { throw new RuntimeException("Missing Bedrock ID in mappings!: " + bedrockId); } - int stackSize = entry.getValue().get("stack_size") == null ? 64 : entry.getValue().get("stack_size").intValue(); + JsonNode stackSizeNode = entry.getValue().get("stack_size"); + int stackSize = stackSizeNode == null ? 64 : stackSizeNode.intValue(); if (entry.getValue().has("tool_type")) { if (entry.getValue().has("tool_tier")) { ITEM_ENTRIES.put(itemIndex, new ToolItemEntry( From ff69752d2cd434985dd44c74d960852aae5363ad Mon Sep 17 00:00:00 2001 From: AJ Ferguson Date: Mon, 4 Jan 2021 15:16:21 -0900 Subject: [PATCH 37/39] Remove unnecessary is_block null check --- .../connector/network/translators/item/ItemRegistry.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java index adb91aa3..dea9b5c9 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java @@ -156,7 +156,7 @@ public class ItemRegistry { entry.getValue().get("bedrock_data").intValue(), entry.getValue().get("tool_type").textValue(), entry.getValue().get("tool_tier").textValue(), - entry.getValue().get("is_block") != null && entry.getValue().get("is_block").booleanValue(), + entry.getValue().get("is_block").booleanValue(), stackSize)); } else { ITEM_ENTRIES.put(itemIndex, new ToolItemEntry( @@ -170,7 +170,7 @@ public class ItemRegistry { ITEM_ENTRIES.put(itemIndex, new ItemEntry( entry.getKey(), bedrockIdentifier, itemIndex, bedrockId, entry.getValue().get("bedrock_data").intValue(), - entry.getValue().get("is_block") != null && entry.getValue().get("is_block").booleanValue(), + entry.getValue().get("is_block").booleanValue(), stackSize)); } switch (entry.getKey()) { From a88678a5c10ecdaadb75cec318306b48260fe1c1 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Mon, 4 Jan 2021 22:15:55 -0500 Subject: [PATCH 38/39] Add manual recipes as Recipe classes for future usage --- .../translators/item/ItemRegistry.java | 2 +- .../translators/item/RecipeRegistry.java | 92 ++++++++++++++++--- .../translators/TippedArrowTranslator.java | 1 + .../java/JavaDeclareRecipesTranslator.java | 2 +- connector/src/main/resources/mappings | 2 +- 5 files changed, 84 insertions(+), 15 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java index dea9b5c9..4237f7d6 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java @@ -56,7 +56,7 @@ public class ItemRegistry { * A list of all identifiers that only exist on Java. Used to prevent creative items from becoming these unintentionally. */ private static final List JAVA_ONLY_ITEMS = Arrays.asList("minecraft:spectral_arrow", "minecraft:debug_stick", - "minecraft:knowledge_book"); + "minecraft:knowledge_book", "minecraft:tipped_arrow", "minecraft:furnace_minecart"); public static final ItemData[] CREATIVE_ITEMS; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/RecipeRegistry.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/RecipeRegistry.java index 4c6a872f..cb866e6b 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/RecipeRegistry.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/RecipeRegistry.java @@ -26,13 +26,25 @@ package org.geysermc.connector.network.translators.item; import com.fasterxml.jackson.databind.JsonNode; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; +import com.github.steveice10.mc.protocol.data.game.recipe.Ingredient; +import com.github.steveice10.mc.protocol.data.game.recipe.Recipe; +import com.github.steveice10.mc.protocol.data.game.recipe.RecipeType; +import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapedRecipeData; +import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapelessRecipeData; +import com.nukkitx.nbt.NbtMap; +import com.nukkitx.nbt.NbtUtils; import com.nukkitx.protocol.bedrock.data.inventory.CraftingData; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.utils.FileUtils; import org.geysermc.connector.utils.LanguageUtils; +import java.io.ByteArrayInputStream; +import java.io.IOException; import java.io.InputStream; import java.util.*; @@ -47,7 +59,7 @@ public class RecipeRegistry { */ public static int LAST_RECIPE_NET_ID = 0; - //public static final Int2ObjectMap + public static final Int2ObjectMap ALL_CRAFTING_RECIPES = new Int2ObjectOpenHashMap<>(); /** * A list of all possible leather armor dyeing recipes. @@ -131,7 +143,7 @@ public class RecipeRegistry { throw new AssertionError(LanguageUtils.getLocaleStringLog("geyser.toolbox.fail.runtime_java"), e); } - for (JsonNode entry: items.get("leather_armor")) { + for (JsonNode entry : items.get("leather_armor")) { // This won't be perfect, as we can't possibly send every leather input for every kind of color // But it does display the correct output from a base leather armor, and besides visuals everything works fine LEATHER_DYEING_RECIPES.add(getCraftingDataFromJsonNode(entry)); @@ -151,6 +163,7 @@ public class RecipeRegistry { for (JsonNode entry : items.get("tipped_arrows")) { TIPPED_ARROW_RECIPES.add(getCraftingDataFromJsonNode(entry)); } + System.out.println(ALL_CRAFTING_RECIPES); } /** @@ -159,9 +172,13 @@ public class RecipeRegistry { * @return the {@link CraftingData} to send to the Bedrock client. */ private static CraftingData getCraftingDataFromJsonNode(JsonNode node) { - ItemData output = ItemRegistry.getBedrockItemFromJson(node.get("output").get(0)); + int netId = LAST_RECIPE_NET_ID++; + int type = node.get("bedrockRecipeType").asInt(); + JsonNode outputNode = node.get("output"); + ItemEntry outputEntry = ItemRegistry.getItemEntry(outputNode.get("identifier").asText()); + ItemData output = getBedrockItemFromIdentifierJson(outputEntry, outputNode); UUID uuid = UUID.randomUUID(); - if (node.get("type").asInt() == 1) { + if (type == 1) { // Shaped recipe List shape = new ArrayList<>(); // Get the shape of the recipe @@ -171,10 +188,12 @@ public class RecipeRegistry { // In recipes.json each recipe is mapped by a letter Map letterToRecipe = new HashMap<>(); - Iterator> iterator = node.get("input").fields(); + Iterator> iterator = node.get("inputs").fields(); while (iterator.hasNext()) { Map.Entry entry = iterator.next(); - letterToRecipe.put(entry.getKey(), ItemRegistry.getBedrockItemFromJson(entry.getValue())); + JsonNode inputNode = entry.getValue(); + ItemEntry inputEntry = ItemRegistry.getItemEntry(inputNode.get("identifier").asText()); + letterToRecipe.put(entry.getKey(), getBedrockItemFromIdentifierJson(inputEntry, inputNode)); } List inputs = new ArrayList<>(shape.size() * shape.get(0).length()); @@ -188,20 +207,69 @@ public class RecipeRegistry { } } + /* Convert into a Java recipe class for autocrafting */ + List ingredients = new ArrayList<>(); + for (ItemData input : inputs) { + ingredients.add(new Ingredient(new ItemStack[]{ItemTranslator.translateToJava(input)})); + } + ShapedRecipeData data = new ShapedRecipeData(shape.get(0).length(), shape.size(), "crafting_table", + ingredients.toArray(new Ingredient[0]), ItemTranslator.translateToJava(output)); + Recipe recipe = new Recipe(RecipeType.CRAFTING_SHAPED, "", data); + ALL_CRAFTING_RECIPES.put(netId, recipe); + /* Convert end */ + return CraftingData.fromShaped(uuid.toString(), shape.get(0).length(), shape.size(), - inputs, Collections.singletonList(output), uuid, "crafting_table", 0, LAST_RECIPE_NET_ID++); + inputs, Collections.singletonList(output), uuid, "crafting_table", 0, netId); } List inputs = new ObjectArrayList<>(); - for (JsonNode entry : node.get("input")) { - inputs.add(ItemRegistry.getBedrockItemFromJson(entry)); + for (JsonNode entry : node.get("inputs")) { + ItemEntry inputEntry = ItemRegistry.getItemEntry(entry.get("identifier").asText()); + inputs.add(getBedrockItemFromIdentifierJson(inputEntry, entry)); } - if (node.get("type").asInt() == 5) { + + /* Convert into a Java Recipe class for autocrafting */ + List ingredients = new ArrayList<>(); + for (ItemData input : inputs) { + ingredients.add(new Ingredient(new ItemStack[]{ItemTranslator.translateToJava(input)})); + } + ShapelessRecipeData data = new ShapelessRecipeData("crafting_table", + ingredients.toArray(new Ingredient[0]), ItemTranslator.translateToJava(output)); + Recipe recipe = new Recipe(RecipeType.CRAFTING_SHAPELESS, "", data); + ALL_CRAFTING_RECIPES.put(netId, recipe); + /* Convert end */ + + if (type == 5) { // Shulker box return CraftingData.fromShulkerBox(uuid.toString(), - inputs, Collections.singletonList(output), uuid, "crafting_table", 0, LAST_RECIPE_NET_ID++); + inputs, Collections.singletonList(output), uuid, "crafting_table", 0, netId); } return CraftingData.fromShapeless(uuid.toString(), - inputs, Collections.singletonList(output), uuid, "crafting_table", 0, LAST_RECIPE_NET_ID++); + inputs, Collections.singletonList(output), uuid, "crafting_table", 0, netId); + } + + private static ItemData getBedrockItemFromIdentifierJson(ItemEntry itemEntry, JsonNode itemNode) { + int count = 1; + short damage = 0; + NbtMap tag = null; + JsonNode damageNode = itemNode.get("bedrockDamage"); + if (damageNode != null) { + damage = damageNode.numberValue().shortValue(); + } + JsonNode countNode = itemNode.get("count"); + if (countNode != null) { + count = countNode.asInt(); + } + JsonNode nbtNode = itemNode.get("bedrockNbt"); + if (nbtNode != null) { + byte[] bytes = Base64.getDecoder().decode(nbtNode.asText()); + ByteArrayInputStream bais = new ByteArrayInputStream(bytes); + try { + tag = (NbtMap) NbtUtils.createReaderLE(bais).readTag(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return ItemData.of(itemEntry.getBedrockId(), damage, count, tag); } public static void init() { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/TippedArrowTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/TippedArrowTranslator.java index dd151dcd..c33b71ac 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/TippedArrowTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/TippedArrowTranslator.java @@ -70,6 +70,7 @@ public class TippedArrowTranslator extends ItemTranslator { @Override public ItemStack translateToJava(ItemData itemData, ItemEntry itemEntry) { + GeyserConnector.getInstance().getLogger().warning(itemData.toString() + " " + itemEntry.getJavaIdentifier()); TippedArrowPotion tippedArrowPotion = TippedArrowPotion.getByBedrockId(itemData.getDamage()); ItemStack itemStack = super.translateToJava(itemData, itemEntry); if (tippedArrowPotion != null) { 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 2565c6ab..3b4f14d7 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 @@ -59,7 +59,7 @@ public class JavaDeclareRecipesTranslator extends PacketTranslator recipeMap = new Int2ObjectOpenHashMap<>(); + Int2ObjectMap recipeMap = new Int2ObjectOpenHashMap<>(RecipeRegistry.ALL_CRAFTING_RECIPES); Int2ObjectMap> unsortedStonecutterData = new Int2ObjectOpenHashMap<>(); CraftingDataPacket craftingDataPacket = new CraftingDataPacket(); craftingDataPacket.setCleanRecipes(true); diff --git a/connector/src/main/resources/mappings b/connector/src/main/resources/mappings index 62e39aca..07f65c38 160000 --- a/connector/src/main/resources/mappings +++ b/connector/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit 62e39acaf3859da86189a76fff57b30c2403ef3d +Subproject commit 07f65c3803dcd3f83358ee574e54bf129cad0840 From c8016647f21c67ee2f7452c30b5be9a40a65610c Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Tue, 5 Jan 2021 19:08:54 -0500 Subject: [PATCH 39/39] Clean up and add mobile button for horse opening --- .../entity/player/BedrockInteractTranslator.java | 12 +++++++++--- .../network/translators/item/RecipeRegistry.java | 5 ++++- .../item/translators/TippedArrowTranslator.java | 1 - 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockInteractTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockInteractTranslator.java index c7c596d8..ca71a197 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockInteractTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockInteractTranslator.java @@ -39,6 +39,7 @@ import com.nukkitx.protocol.bedrock.packet.InteractPacket; import lombok.Getter; import org.geysermc.connector.entity.Entity; import org.geysermc.connector.entity.living.animal.horse.AbstractHorseEntity; +import org.geysermc.connector.entity.living.animal.horse.HorseEntity; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.inventory.GeyserItemStack; import org.geysermc.connector.network.session.GeyserSession; @@ -212,6 +213,11 @@ public class BedrockInteractTranslator extends PacketTranslator case SKELETON_HORSE: case TRADER_LLAMA: case ZOMBIE_HORSE: + boolean tamed = entityMetadata.getFlags().getFlag(EntityFlag.TAMED); + if (session.isSneaking() && tamed && (interactEntity instanceof HorseEntity || entityMetadata.getFlags().getFlag(EntityFlag.CHESTED))) { + interactiveTag = InteractiveTag.OPEN_CONTAINER; + break; + } // have another switch statement as, while these share mount attributes they don't share food switch (interactEntity.getEntityType()) { case LLAMA: @@ -230,9 +236,9 @@ public class BedrockInteractTranslator extends PacketTranslator } if (!entityMetadata.getFlags().getFlag(EntityFlag.BABY)) { // Can't ride a baby - if (entityMetadata.getFlags().getFlag(EntityFlag.TAMED)) { + if (tamed) { interactiveTag = InteractiveTag.RIDE_HORSE; - } else if (!entityMetadata.getFlags().getFlag(EntityFlag.TAMED) && itemEntry.equals(ItemEntry.AIR)) { + } else if (itemEntry.equals(ItemEntry.AIR)) { // Can't hide an untamed entity without having your hand empty interactiveTag = InteractiveTag.MOUNT; } @@ -351,7 +357,7 @@ public class BedrockInteractTranslator extends PacketTranslator } else { if (!session.getPlayerEntity().getMetadata().getString(EntityData.INTERACTIVE_TAG).isEmpty()) { // No interactive tag should be sent - session.getPlayerEntity().getMetadata().remove(EntityData.INTERACTIVE_TAG); + session.getPlayerEntity().getMetadata().put(EntityData.INTERACTIVE_TAG, ""); session.getPlayerEntity().updateBedrockMetadata(session); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/RecipeRegistry.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/RecipeRegistry.java index cb866e6b..110014bf 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/RecipeRegistry.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/RecipeRegistry.java @@ -59,6 +59,10 @@ public class RecipeRegistry { */ public static int LAST_RECIPE_NET_ID = 0; + /** + * A list of all the following crafting recipes, but in a format understood by Java servers. + * Used for console autocrafting. + */ public static final Int2ObjectMap ALL_CRAFTING_RECIPES = new Int2ObjectOpenHashMap<>(); /** @@ -163,7 +167,6 @@ public class RecipeRegistry { for (JsonNode entry : items.get("tipped_arrows")) { TIPPED_ARROW_RECIPES.add(getCraftingDataFromJsonNode(entry)); } - System.out.println(ALL_CRAFTING_RECIPES); } /** diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/TippedArrowTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/TippedArrowTranslator.java index c33b71ac..dd151dcd 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/TippedArrowTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/TippedArrowTranslator.java @@ -70,7 +70,6 @@ public class TippedArrowTranslator extends ItemTranslator { @Override public ItemStack translateToJava(ItemData itemData, ItemEntry itemEntry) { - GeyserConnector.getInstance().getLogger().warning(itemData.toString() + " " + itemEntry.getJavaIdentifier()); TippedArrowPotion tippedArrowPotion = TippedArrowPotion.getByBedrockId(itemData.getDamage()); ItemStack itemStack = super.translateToJava(itemData, itemEntry); if (tippedArrowPotion != null) {