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] 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)