From 370f3c18ba4e014a8894627da9a2ae662c791b3d Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 10 Mar 2021 13:54:09 -0500 Subject: [PATCH 01/19] Use compute for determining if a lectern if present Reduces having to call both containsKey and get. --- .../world/block/BlockStateValues.java | 9 ++++- .../geysermc/connector/utils/ChunkUtils.java | 37 ++++++++++--------- 2 files changed, 28 insertions(+), 18 deletions(-) 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 ebc90b722..ff51562b9 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 @@ -29,6 +29,7 @@ import com.fasterxml.jackson.databind.JsonNode; import it.unimi.dsi.fastutil.ints.*; import java.util.Map; +import java.util.function.BiFunction; /** * Used for block entities if the Java block state contains Bedrock block information. @@ -199,7 +200,13 @@ public class BlockStateValues { return FLOWER_POT_VALUES; } - public static Int2BooleanMap getLecternBookStates() { + /** + * This returns a Map interface so IntelliJ doesn't complain about {@link Int2BooleanMap#compute(int, BiFunction)} + * not returning null. + * + * @return the lectern book state map pointing to book present state + */ + public static Map getLecternBookStates() { return LECTERN_BOOK_STATES; } 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 b3a31e1ab..7d5b56615 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java @@ -372,27 +372,30 @@ 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 - NbtMap newLecternTag; - if (newLecternHasBook) { - newLecternTag = session.getConnector().getWorldManager().getLecternDataAt(session, position.getX(), position.getY(), position.getZ(), false); + BlockStateValues.getLecternBookStates().compute(blockState, (key, newLecternHasBook) -> { + // Determine if this block is a lectern + if (newLecternHasBook != null) { + boolean lecternCachedHasBook = session.getLecternCache().contains(position); + if (!session.getConnector().getWorldManager().shouldExpectLecternHandled() && lecternCachedHasBook != newLecternHasBook) { + // Refresh the block entirely - it either has a book or no longer has a book + 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 { - session.getLecternCache().remove(position); - newLecternTag = LecternInventoryTranslator.getBaseLecternTag(position.getX(), position.getY(), position.getZ(), 0).build(); + // As of right now, no tag can be added asynchronously + session.getConnector().getWorldManager().getLecternDataAt(session, position.getX(), position.getY(), position.getZ(), false); } - 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); + // Lectern has been destroyed, if it existed + session.getLecternCache().remove(position); } - } else { - // Lectern has been destroyed, if it existed - session.getLecternCache().remove(position); - } + return newLecternHasBook; + }); // 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 From e4934152417f4f127f0f85eb11e1159ed62d27a0 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 10 Mar 2021 14:43:54 -0500 Subject: [PATCH 02/19] More cartography parity fixes --- .../inventory/InventoryTranslator.java | 25 +++++++++++++------ .../CartographyInventoryTranslator.java | 8 +++--- 2 files changed, 22 insertions(+), 11 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 1eef679f5..6ef214067 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 @@ -357,17 +357,28 @@ public abstract class InventoryTranslator { if (inventory instanceof CartographyContainer) { // TODO add this for more inventories? Only seems to glitch out the cartography table, though. ConsumeStackRequestActionData consumeData = (ConsumeStackRequestActionData) action; + int sourceSlot = bedrockSlotToJava(consumeData.getSource()); - if (sourceSlot == 0 && inventory.getItem(1).isEmpty()) { - // Java doesn't allow an item to be renamed; this is why CARTOGRAPHY_ADDITIONAL could remain empty for Bedrock - // We check this during slot 0 since setting the inventory slots here messes up shouldRejectItemPlace + if ((sourceSlot == 0 && inventory.getItem(1).isEmpty()) || (sourceSlot == 1 && inventory.getItem(0).isEmpty())) { + // Java doesn't allow an item to be renamed; this is why one of the slots could remain empty for Bedrock + // We check this now since setting the inventory slots here messes up shouldRejectItemPlace return rejectRequest(request, false); } - GeyserItemStack item = inventory.getItem(sourceSlot); - item.setAmount(item.getAmount() - consumeData.getCount()); - if (item.isEmpty()) { - inventory.setItem(sourceSlot, GeyserItemStack.EMPTY, session); + if (sourceSlot == 1) { + // Decrease the item count, but only after both slots are checked. + // Otherwise, the slot 1 check will fail + GeyserItemStack item = inventory.getItem(sourceSlot); + item.setAmount(item.getAmount() - consumeData.getCount()); + if (item.isEmpty()) { + inventory.setItem(sourceSlot, GeyserItemStack.EMPTY, session); + } + + GeyserItemStack itemZero = inventory.getItem(0); + itemZero.setAmount(itemZero.getAmount() - consumeData.getCount()); + if (itemZero.isEmpty()) { + inventory.setItem(0, GeyserItemStack.EMPTY, session); + } } affectedSlots.add(sourceSlot); } 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 319d9ec0a..f0f8b6d84 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 @@ -46,13 +46,13 @@ public class CartographyInventoryTranslator extends AbstractBlockInventoryTransl public boolean shouldRejectItemPlace(GeyserSession session, Inventory inventory, ContainerSlotType bedrockSourceContainer, int javaSourceSlot, ContainerSlotType bedrockDestinationContainer, int javaDestinationSlot) { if (javaDestinationSlot == 0) { - // Bedrock Edition can use paper in slot 0 + // Bedrock Edition can use paper or an empty map in slot 0 GeyserItemStack itemStack = javaSourceSlot == -1 ? session.getPlayerInventory().getCursor() : inventory.getItem(javaSourceSlot); - return itemStack.getItemEntry().getJavaIdentifier().equals("minecraft:paper"); + return itemStack.getItemEntry().getJavaIdentifier().equals("minecraft:paper") || itemStack.getItemEntry().getJavaIdentifier().equals("minecraft:map"); } else if (javaDestinationSlot == 1) { - // Bedrock Edition can use a compass to create locator maps in the ADDITIONAL slot + // Bedrock Edition can use a compass to create locator maps, or use a filled map, in the ADDITIONAL slot GeyserItemStack itemStack = javaSourceSlot == -1 ? session.getPlayerInventory().getCursor() : inventory.getItem(javaSourceSlot); - return itemStack.getItemEntry().getJavaIdentifier().equals("minecraft:compass"); + return itemStack.getItemEntry().getJavaIdentifier().equals("minecraft:compass") || itemStack.getItemEntry().getJavaIdentifier().equals("minecraft:filled_map"); } return false; } From 09105e00b21749e78f79993fa6d5e9d2d9c63f45 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Thu, 11 Mar 2021 21:11:46 -0500 Subject: [PATCH 03/19] Fix item frame dropping on 1.16.210 (#2029) For whatever reason, item frames use the PlayerActionPacket in any non-creative gamemode, but creative mode keeps existing behavior. This has the benefit of fixing adventure mode item frame item dropping. --- .../BedrockItemFrameDropItemTranslator.java | 9 ++++++--- .../entity/player/BedrockActionTranslator.java | 18 +++++++++++++++--- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockItemFrameDropItemTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockItemFrameDropItemTranslator.java index e7915bff3..959d6dc29 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockItemFrameDropItemTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockItemFrameDropItemTranslator.java @@ -28,20 +28,23 @@ package org.geysermc.connector.network.translators.bedrock; import com.github.steveice10.mc.protocol.data.game.entity.player.Hand; import com.github.steveice10.mc.protocol.data.game.entity.player.InteractAction; import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerInteractEntityPacket; -import com.nukkitx.math.vector.Vector3i; import com.nukkitx.protocol.bedrock.packet.ItemFrameDropItemPacket; import org.geysermc.connector.entity.ItemFrameEntity; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; +/** + * Pre-1.16.210: used for both survival and creative item frame item removal + * + * 1.16.210: only used in creative. + */ @Translator(packet = ItemFrameDropItemPacket.class) public class BedrockItemFrameDropItemTranslator extends PacketTranslator { @Override public void translate(ItemFrameDropItemPacket packet, GeyserSession session) { - Vector3i position = Vector3i.from(packet.getBlockPosition().getX(), packet.getBlockPosition().getY(), packet.getBlockPosition().getZ()); - ClientPlayerInteractEntityPacket interactPacket = new ClientPlayerInteractEntityPacket((int) ItemFrameEntity.getItemFrameEntityId(session, position), + ClientPlayerInteractEntityPacket interactPacket = new ClientPlayerInteractEntityPacket((int) ItemFrameEntity.getItemFrameEntityId(session, packet.getBlockPosition()), InteractAction.ATTACK, Hand.MAIN_HAND, session.isSneaking()); session.sendDownstreamPacket(interactPacket); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockActionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockActionTranslator.java index e837a036a..3079b668d 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockActionTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockActionTranslator.java @@ -27,12 +27,11 @@ package org.geysermc.connector.network.translators.bedrock.entity.player; 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.PlayerAction; -import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerState; +import com.github.steveice10.mc.protocol.data.game.entity.player.*; import com.github.steveice10.mc.protocol.data.game.world.block.BlockFace; import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerAbilitiesPacket; import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerActionPacket; +import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerInteractEntityPacket; import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerStatePacket; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.nukkitx.math.vector.Vector3i; @@ -44,6 +43,7 @@ import com.nukkitx.protocol.bedrock.packet.LevelEventPacket; import com.nukkitx.protocol.bedrock.packet.PlayStatusPacket; import com.nukkitx.protocol.bedrock.packet.PlayerActionPacket; import org.geysermc.connector.entity.Entity; +import org.geysermc.connector.entity.ItemFrameEntity; import org.geysermc.connector.inventory.PlayerInventory; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; @@ -185,6 +185,18 @@ public class BedrockActionTranslator extends PacketTranslator Date: Thu, 11 Mar 2021 17:43:08 -0900 Subject: [PATCH 04/19] Fix loom output --- .../translators/LoomInventoryTranslator.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) 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 38758c5f8..17c93c15b 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 @@ -44,6 +44,7 @@ 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.translators.BannerTranslator; @@ -144,7 +145,7 @@ public class LoomInventoryTranslator extends AbstractBlockInventoryTranslator { ClientClickWindowButtonPacket packet = new ClientClickWindowButtonPacket(inventory.getId(), index); session.sendDownstreamPacket(packet); - GeyserItemStack inputCopy = inventory.getItem(0).copy(); + GeyserItemStack inputCopy = inventory.getItem(0).copy(1); inputCopy.setNetId(session.getNextItemNetId()); // Add the pattern manually, for better item synchronization if (inputCopy.getNbt() == null) { @@ -219,4 +220,12 @@ public class LoomInventoryTranslator extends AbstractBlockInventoryTranslator { } return super.javaSlotToBedrock(slot); } + + @Override + public SlotType getSlotType(int javaSlot) { + if (javaSlot == 3) { + return SlotType.OUTPUT; + } + return super.getSlotType(javaSlot); + } } From 821e3126ea3deb1cea616741a7eed04eb6e2dac7 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Fri, 12 Mar 2021 11:51:37 -0500 Subject: [PATCH 05/19] Handful of review fixes --- .../inventory/Generic3X3Container.java | 5 ++- .../BedrockLecternUpdateTranslator.java | 4 +-- .../inventory/InventoryTranslator.java | 33 ++++++++++++------- .../LecternInventoryTranslator.java | 10 +----- .../AbstractHorseInventoryTranslator.java | 3 -- .../inventory/updater/InventoryUpdater.java | 2 +- .../java/window/JavaSetSlotTranslator.java | 1 - .../world/block/BlockTranslator.java | 6 ++-- 8 files changed, 33 insertions(+), 31 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/inventory/Generic3X3Container.java b/connector/src/main/java/org/geysermc/connector/inventory/Generic3X3Container.java index 8c89cdeb6..080e11982 100644 --- a/connector/src/main/java/org/geysermc/connector/inventory/Generic3X3Container.java +++ b/connector/src/main/java/org/geysermc/connector/inventory/Generic3X3Container.java @@ -27,10 +27,13 @@ package org.geysermc.connector.inventory; import com.github.steveice10.mc.protocol.data.game.window.WindowType; import lombok.Getter; +import org.geysermc.connector.network.session.GeyserSession; public class Generic3X3Container extends Container { /** - * Whether we need to set the container type as {@link com.nukkitx.protocol.bedrock.data.inventory.ContainerType#DROPPER} + * Whether we need to set the container type as {@link com.nukkitx.protocol.bedrock.data.inventory.ContainerType#DROPPER}. + * + * Used at {@link org.geysermc.connector.network.translators.inventory.translators.Generic3X3InventoryTranslator#openInventory(GeyserSession, Inventory)} */ @Getter private boolean isDropper = false; 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 99dcebed9..10af0b610 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 @@ -56,7 +56,7 @@ public class BedrockLecternUpdateTranslator extends PacketTranslator currentJavaPage) { for (int i = currentJavaPage; i < newJavaPage; i++) { ClientClickWindowButtonPacket clickButtonPacket = new ClientClickWindowButtonPacket(session.getOpenInventory().getId(), 2); 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 6ef214067..6ec90d24d 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 @@ -108,8 +108,8 @@ public abstract class InventoryTranslator { public abstract void updateInventory(GeyserSession session, Inventory inventory); public abstract void updateSlot(GeyserSession session, Inventory inventory, int slot); public abstract int bedrockSlotToJava(StackRequestSlotInfoData slotInfoData); - public abstract int javaSlotToBedrock(int javaSlot); //TODO - public abstract BedrockContainerSlot javaSlotToBedrockContainer(int javaSlot); //TODO + public abstract int javaSlotToBedrock(int javaSlot); + public abstract BedrockContainerSlot javaSlotToBedrockContainer(int javaSlot); public abstract SlotType getSlotType(int javaSlot); public abstract Inventory createInventory(String name, int windowId, WindowType windowType, PlayerInventory playerInventory); @@ -138,7 +138,7 @@ public abstract class InventoryTranslator { * If {@link #shouldHandleRequestFirst(StackRequestActionData, Inventory)} returns true, this will be called */ public ItemStackResponsePacket.Response translateSpecialRequest(GeyserSession session, Inventory inventory, ItemStackRequest request) { - return null; + return rejectRequest(request); } public void translateRequests(GeyserSession session, Inventory inventory, List requests) { @@ -151,15 +151,22 @@ public abstract class InventoryTranslator { if (shouldHandleRequestFirst(firstAction, inventory)) { // Some special request that shouldn't be processed normally response = translateSpecialRequest(session, inventory, request); - } else if (firstAction.getType() == StackRequestActionType.CRAFT_RECIPE) { - response = translateCraftingRequest(session, inventory, request); - } else if (firstAction.getType() == StackRequestActionType.CRAFT_RECIPE_AUTO) { - response = translateAutoCraftingRequest(session, inventory, request); - } else if (firstAction.getType() == StackRequestActionType.CRAFT_CREATIVE) { - // This is also used for pulling items out of creative - response = translateCreativeRequest(session, inventory, request); } else { - response = translateRequest(session, inventory, request); + switch (firstAction.getType()) { + case CRAFT_RECIPE: + response = translateCraftingRequest(session, inventory, request); + break; + case CRAFT_RECIPE_AUTO: + response = translateAutoCraftingRequest(session, inventory, request); + break; + case CRAFT_CREATIVE: + // This is also used for pulling items out of creative + response = translateCreativeRequest(session, inventory, request); + break; + default: + response = translateRequest(session, inventory, request); + break; + } } } else { response = rejectRequest(request); @@ -693,8 +700,10 @@ public abstract class InventoryTranslator { return acceptRequest(request, makeContainerEntries(session, inventory, plan.getAffectedSlots())); } + /** + * Handled in {@link PlayerInventoryTranslator} + */ public ItemStackResponsePacket.Response translateCreativeRequest(GeyserSession session, Inventory inventory, ItemStackRequest request) { - // Handled in PlayerInventoryTranslator return rejectRequest(request); } 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 dbbc418ba..c08dfd995 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 @@ -51,22 +51,19 @@ public class LecternInventoryTranslator extends BaseInventoryTranslator { public LecternInventoryTranslator() { super(1); - this.updater = new LecternInventoryUpdater(); + this.updater = new InventoryUpdater(); } @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 @@ -81,7 +78,6 @@ public class LecternInventoryTranslator extends BaseInventoryTranslator { @Override public void updateInventory(GeyserSession session, Inventory inventory) { - } @Override @@ -171,8 +167,4 @@ public class LecternInventoryTranslator extends BaseInventoryTranslator { } return builder; } - - private static class LecternInventoryUpdater extends InventoryUpdater { - - } } 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 6c6c9a0c2..0e365aca1 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 @@ -41,17 +41,14 @@ public abstract class AbstractHorseInventoryTranslator extends BaseInventoryTran @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 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 d7c137177..e94c0944b 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 @@ -35,7 +35,7 @@ import org.geysermc.connector.network.translators.inventory.InventoryTranslator; import java.util.Arrays; -public abstract class InventoryUpdater { +public class InventoryUpdater { public void updateInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) { ItemData[] bedrockItems = new ItemData[36]; for (int i = 0; i < 36; i++) { 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 b5978ba76..a0e9901f3 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 @@ -134,7 +134,6 @@ public class JavaSetSlotTranslator extends PacketTranslator height += -firstRow + 1; width += -firstCol + 1; - //TODO recipes: for (Recipe recipe : session.getCraftingRecipes().values()) { if (recipe.getType() == RecipeType.CRAFTING_SHAPED) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator.java index ec1c79950..057c74d2b 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator.java @@ -112,8 +112,6 @@ public abstract class BlockTranslator { */ private final Map javaIdentifierToBedrockTag; - private static final int BLOCK_STATE_VERSION = 17825808; - /** * Stores the raw blocks JSON until it is no longer needed. */ @@ -413,6 +411,10 @@ public abstract class BlockTranslator { return bedrockWaterId; } + /** + * @return the "block state version" generated in the Bedrock block palette that completes an NBT indication of a + * block state. + */ public abstract int getBlockStateVersion(); public byte[] getEmptyChunkData() { From 23ab69702d56f97358129f305fdb44882506ad91 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Fri, 12 Mar 2021 17:07:29 -0500 Subject: [PATCH 06/19] More review fixes and make beacon more reliable --- .../holder/BlockInventoryHolder.java | 12 +++++++- .../BeaconInventoryTranslator.java | 29 ++++++++++++++++++- .../CartographyInventoryTranslator.java | 16 +++++----- .../java/world/JavaTradeListTranslator.java | 2 ++ 4 files changed, 49 insertions(+), 10 deletions(-) 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 e7bfd90f0..b7f67879b 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 @@ -74,7 +74,7 @@ public class BlockInventoryHolder extends InventoryHolder { // Check to see if there is an existing block we can use that the player just selected. // First, verify that the player's position has not changed, so we don't try to select a block wildly out of range. // (This could be a virtual inventory that the player is opening) - if (session.getLastInteractionPlayerPosition().equals(session.getPlayerEntity().getPosition())) { + if (checkInteractionPosition(session)) { // Then, check to see if the interacted block is valid for this inventory by ensuring the block state identifier is valid int javaBlockId = session.getConnector().getWorldManager().getBlockAt(session, session.getLastInteractionBlockPosition()); String[] javaBlockString = BlockTranslator.getJavaIdBlockMap().inverse().getOrDefault(javaBlockId, "minecraft:air").split("\\["); @@ -101,6 +101,16 @@ public class BlockInventoryHolder extends InventoryHolder { setCustomName(session, position, inventory, defaultJavaBlockState); } + /** + * Will be overwritten in the beacon inventory translator to remove the check, since virtual inventories can't exist. + * + * @return if the player's last interaction position and current position match. Used to ensure that we don't select + * a block to hold the inventory that's wildly out of range. + */ + protected boolean checkInteractionPosition(GeyserSession session) { + return session.getLastInteractionPlayerPosition().equals(session.getPlayerEntity().getPosition()); + } + /** * @return true if this Java block ID can be used for player inventory. */ 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 46c09b66b..5af921f2d 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 @@ -44,13 +44,40 @@ 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.holder.BlockInventoryHolder; import org.geysermc.connector.network.translators.inventory.updater.UIInventoryUpdater; +import org.geysermc.connector.utils.InventoryUtils; import java.util.Collections; public class BeaconInventoryTranslator extends AbstractBlockInventoryTranslator { public BeaconInventoryTranslator() { - super(1, "minecraft:beacon", ContainerType.BEACON, UIInventoryUpdater.INSTANCE); + super(1, new BlockInventoryHolder("minecraft:beacon", ContainerType.BEACON) { + @Override + public void prepareInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) { + if (!session.getConnector().getConfig().isCacheChunks()) { + // Beacons cannot work without knowing their physical location + return; + } + super.prepareInventory(translator, session, inventory); + } + + @Override + protected boolean checkInteractionPosition(GeyserSession session) { + // Since we can't fall back to a virtual inventory, let's make opening one easier + return true; + } + + @Override + public void openInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) { + if (!session.getConnector().getConfig().isCacheChunks() || !((BeaconContainer) inventory).isUsingRealBlock()) { + InventoryUtils.closeInventory(session, inventory.getId(), false); + return; + } + super.openInventory(translator, session, inventory); + } + }, UIInventoryUpdater.INSTANCE); } @Override 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 f0f8b6d84..a3b50dace 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 @@ -59,14 +59,14 @@ public class CartographyInventoryTranslator extends AbstractBlockInventoryTransl @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; + switch (slotInfoData.getContainer()) { + case CARTOGRAPHY_INPUT: + return 0; + case CARTOGRAPHY_ADDITIONAL: + return 1; + case CARTOGRAPHY_RESULT: + case CREATIVE_OUTPUT: + return 2; } return super.bedrockSlotToJava(slotInfoData); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaTradeListTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaTradeListTranslator.java index df8339079..d31dbb617 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaTradeListTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaTradeListTranslator.java @@ -58,6 +58,7 @@ public class JavaTradeListTranslator extends PacketTranslator Date: Sat, 13 Mar 2021 15:01:55 -0500 Subject: [PATCH 07/19] Cleaner ItemEntry retrieval in BedrockInteractTranslator --- .../bedrock/entity/player/BedrockInteractTranslator.java | 3 +-- 1 file changed, 1 insertion(+), 2 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 ca71a1975..e5c83388b 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 @@ -41,7 +41,6 @@ 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; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; @@ -125,7 +124,7 @@ public class BedrockInteractTranslator extends PacketTranslator if (interactEntity == null) return; EntityDataMap entityMetadata = interactEntity.getMetadata(); - ItemEntry itemEntry = session.getPlayerInventory().getItemInHand() == GeyserItemStack.EMPTY ? ItemEntry.AIR : ItemRegistry.getItem(session.getPlayerInventory().getItemInHand().getItemStack()); + ItemEntry itemEntry = session.getPlayerInventory().getItemInHand().getItemEntry(); String javaIdentifierStripped = itemEntry.getJavaIdentifier().replace("minecraft:", ""); // TODO - in the future, update these in the metadata? So the client doesn't have to wiggle their cursor around for it to happen From 48aa586b218b791ead88cb57683e0889ecb682f1 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sat, 13 Mar 2021 18:07:43 -0500 Subject: [PATCH 08/19] More formatting fixes --- .../manager/GeyserSpigotWorldManager.java | 1 + .../connector/inventory/GeyserItemStack.java | 12 ++---- .../BedrockLecternUpdateTranslator.java | 2 + .../player/BedrockInteractTranslator.java | 2 +- .../inventory/InventoryTranslator.java | 37 ++++++++++++------- .../CraftingInventoryTranslator.java | 3 +- .../updater/HorseInventoryUpdater.java | 1 - 7 files changed, 34 insertions(+), 24 deletions(-) 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 13f696fd5..bc76288c5 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 @@ -235,6 +235,7 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager { NbtMap blockEntityTag = lecternTag.build(); BlockEntityUtils.updateBlockEntity(session, blockEntityTag, Vector3i.from(x, y, z)); }; + if (isChunkLoad) { // Delay to ensure the chunk is sent first, and then the lectern data Bukkit.getScheduler().runTaskLater(this.plugin, lecternInfoGet, 5); 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 7cdaf1801..b4e91c1d6 100644 --- a/connector/src/main/java/org/geysermc/connector/inventory/GeyserItemStack.java +++ b/connector/src/main/java/org/geysermc/connector/inventory/GeyserItemStack.java @@ -62,6 +62,10 @@ public class GeyserItemStack { this.netId = netId; } + public static GeyserItemStack from(ItemStack itemStack) { + return itemStack == null ? EMPTY : new GeyserItemStack(itemStack.getId(), itemStack.getAmount(), itemStack.getNbt()); + } + public int getJavaId() { return isEmpty() ? 0 : javaId; } @@ -74,10 +78,6 @@ public class GeyserItemStack { return isEmpty() ? null : nbt; } - public void setNetId(int netId) { - this.netId = netId; - } - public int getNetId() { return isEmpty() ? 0 : netId; } @@ -90,10 +90,6 @@ public class GeyserItemStack { amount -= sub; } - public static GeyserItemStack from(ItemStack itemStack) { - return itemStack == null ? EMPTY : new GeyserItemStack(itemStack.getId(), itemStack.getAmount(), itemStack.getNbt()); - } - public ItemStack getItemStack() { return getItemStack(amount); } 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 10af0b610..ae99fec07 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 @@ -65,6 +65,7 @@ public class BedrockLecternUpdateTranslator extends PacketTranslator 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); + ClientPlayerStatePacket openHorseWindowPacket = new ClientPlayerStatePacket((int) session.getPlayerEntity().getEntityId(), PlayerState.OPEN_HORSE_INVENTORY); session.sendDownstreamPacket(openHorseWindowPacket); } } else { 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 6ec90d24d..5fd44c8b1 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 @@ -39,6 +39,7 @@ import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.*; import com.nukkitx.protocol.bedrock.packet.ItemStackResponsePacket; import it.unimi.dsi.fastutil.ints.*; import lombok.AllArgsConstructor; +import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.inventory.*; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.inventory.click.Click; @@ -171,9 +172,12 @@ public abstract class InventoryTranslator { } else { response = rejectRequest(request); } - if (response.getResult() == ItemStackResponsePacket.ResponseStatus.ERROR) { + + if (response.getResult() != ItemStackResponsePacket.ResponseStatus.OK) { + // Sync our copy of the inventory with Bedrock's to prevent desyncs refresh = true; } + responsePacket.getEntries().add(response); } session.sendUpstreamPacket(responsePacket); @@ -198,11 +202,10 @@ public abstract class InventoryTranslator { transferAction.getSource().getSlot() >= 28 && transferAction.getSource().getSlot() <= 31) { return rejectRequest(request, false); } - session.getConnector().getLogger().error("DEBUG: About to reject TAKE/PLACE request made by " + session.getName()); - 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())); - session.getConnector().getLogger().error("Geyser's record of source slot: " + inventory.getItem(bedrockSlotToJava(transferAction.getSource()))); - session.getConnector().getLogger().error("Geyser's record of destination slot: " + inventory.getItem(bedrockSlotToJava(transferAction.getDestination()))); + if (session.getConnector().getConfig().isDebugMode()) { + session.getConnector().getLogger().error("DEBUG: About to reject TAKE/PLACE request made by " + session.getName()); + dumpStackRequestDetails(session, inventory, transferAction.getSource(), transferAction.getDestination()); + } return rejectRequest(request); } @@ -285,11 +288,10 @@ public abstract class InventoryTranslator { case SWAP: { SwapStackRequestActionData swapAction = (SwapStackRequestActionData) action; if (!(checkNetId(session, inventory, swapAction.getSource()) && checkNetId(session, inventory, swapAction.getDestination()))) { - session.getConnector().getLogger().error("DEBUG: About to reject SWAP request made by " + session.getName()); - session.getConnector().getLogger().error("Source: " + swapAction.getSource().toString() + " Result: " + checkNetId(session, inventory, swapAction.getSource())); - session.getConnector().getLogger().error("Destination: " + swapAction.getDestination().toString() + " Result: " + checkNetId(session, inventory, swapAction.getDestination())); - session.getConnector().getLogger().error("Geyser's record of source slot: " + inventory.getItem(bedrockSlotToJava(swapAction.getSource()))); - session.getConnector().getLogger().error("Geyser's record of destination slot: " + inventory.getItem(bedrockSlotToJava(swapAction.getDestination()))); + if (session.getConnector().getConfig().isDebugMode()) { + session.getConnector().getLogger().error("DEBUG: About to reject SWAP request made by " + session.getName()); + dumpStackRequestDetails(session, inventory, swapAction.getSource(), swapAction.getDestination()); + } return rejectRequest(request); } @@ -756,13 +758,22 @@ public abstract class InventoryTranslator { * as bad (false). */ public static ItemStackResponsePacket.Response rejectRequest(ItemStackRequest request, boolean throwError) { - if (throwError) { - // Currently for debugging, but might be worth it to keep in the future if something goes terribly wrong. + if (throwError && GeyserConnector.getInstance().getConfig().isDebugMode()) { new Throwable("DEBUGGING: ItemStackRequest rejected " + request.toString()).printStackTrace(); } return new ItemStackResponsePacket.Response(ItemStackResponsePacket.ResponseStatus.ERROR, request.getRequestId(), Collections.emptyList()); } + /** + * Print out the contents of an ItemStackRequest, should the net ID check fail. + */ + protected void dumpStackRequestDetails(GeyserSession session, Inventory inventory, StackRequestSlotInfoData source, StackRequestSlotInfoData destination) { + session.getConnector().getLogger().error("Source: " + source.toString() + " Result: " + checkNetId(session, inventory, source)); + session.getConnector().getLogger().error("Destination: " + destination.toString() + " Result: " + checkNetId(session, inventory, destination)); + session.getConnector().getLogger().error("Geyser's record of source slot: " + inventory.getItem(bedrockSlotToJava(source))); + session.getConnector().getLogger().error("Geyser's record of destination slot: " + inventory.getItem(bedrockSlotToJava(destination))); + } + public boolean checkNetId(GeyserSession session, Inventory inventory, StackRequestSlotInfoData slotInfoData) { int netId = slotInfoData.getStackNetworkId(); // "In my testing, sometimes the client thinks the netId of an item in the crafting grid is 1, even though we never said it was. 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 81769c00a..363c9b702 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 @@ -39,8 +39,9 @@ public class CraftingInventoryTranslator extends AbstractBlockInventoryTranslato @Override public SlotType getSlotType(int javaSlot) { - if (javaSlot == 0) + if (javaSlot == 0) { return SlotType.OUTPUT; + } return SlotType.NORMAL; } 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 d238b4148..db067a74c 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 @@ -64,5 +64,4 @@ public class HorseInventoryUpdater extends InventoryUpdater { session.sendUpstreamPacket(slotPacket); return true; } - } From e71ffa455f86e7032b4ea5095dae98412d96d288 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sat, 13 Mar 2021 23:09:37 -0500 Subject: [PATCH 09/19] Fix brewing stand fuel input --- .../inventory/translators/BrewingInventoryTranslator.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 index 992a74511..c54722849 100644 --- 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 @@ -91,6 +91,8 @@ public class BrewingInventoryTranslator extends AbstractBlockInventoryTranslator return 3; case 3: return 0; + case 4: + return 4; } return super.javaSlotToBedrock(slot); } @@ -105,7 +107,7 @@ public class BrewingInventoryTranslator extends AbstractBlockInventoryTranslator case 3: return new BedrockContainerSlot(ContainerSlotType.BREWING_INPUT, 0); case 4: - return new BedrockContainerSlot(ContainerSlotType.BREWING_INPUT, 0); + return new BedrockContainerSlot(ContainerSlotType.BREWING_FUEL, 4); } return super.javaSlotToBedrockContainer(slot); } From ba64a7a4892670486e0bb77668ef0c957426170d Mon Sep 17 00:00:00 2001 From: toinouH Date: Sun, 14 Mar 2021 16:37:10 +0100 Subject: [PATCH 10/19] Update Adventure from 4.5.0 to 4.7.0 (#2037) * Update Adventure from 4.5.0 to 4.7.0 * Fix test failure due to changes in Adventure color mapping Co-authored-by: rtm516 --- connector/pom.xml | 2 +- .../network/translators/chat/MessageTranslatorTest.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/connector/pom.xml b/connector/pom.xml index fb253117c..cb528e27b 100644 --- a/connector/pom.xml +++ b/connector/pom.xml @@ -13,7 +13,7 @@ 4.1.59.Final 8.5.2 - 4.5.0 + 4.7.0 diff --git a/connector/src/test/java/org/geysermc/connector/network/translators/chat/MessageTranslatorTest.java b/connector/src/test/java/org/geysermc/connector/network/translators/chat/MessageTranslatorTest.java index bbad2394d..7052123fe 100644 --- a/connector/src/test/java/org/geysermc/connector/network/translators/chat/MessageTranslatorTest.java +++ b/connector/src/test/java/org/geysermc/connector/network/translators/chat/MessageTranslatorTest.java @@ -46,8 +46,8 @@ public class MessageTranslatorTest { // RGB downgrade test messages.put("{\"extra\":[{\"text\":\" \"},{\"color\":\"gold\",\"text\":\"The \"},{\"color\":\"#E14248\",\"obfuscated\":true,\"text\":\"||\"},{\"color\":\"#3AA9FF\",\"bold\":true,\"text\":\"CubeCraft\"},{\"color\":\"#E14248\",\"obfuscated\":true,\"text\":\"||\"},{\"color\":\"gold\",\"text\":\" Network \"},{\"color\":\"green\",\"text\":\"[1.8/1.9+]\\n \"},{\"color\":\"#f5e342\",\"text\":\"✦ \"},{\"color\":\"#b042f5\",\"bold\":true,\"text\":\"N\"},{\"color\":\"#c142f5\",\"bold\":true,\"text\":\"E\"},{\"color\":\"#d342f5\",\"bold\":true,\"text\":\"W\"},{\"color\":\"#e442f5\",\"bold\":true,\"text\":\":\"},{\"color\":\"#f542f5\",\"bold\":true,\"text\":\" \"},{\"color\":\"#bcf542\",\"bold\":true,\"text\":\"A\"},{\"color\":\"#acee3f\",\"bold\":true,\"text\":\"M\"},{\"color\":\"#9ce73c\",\"bold\":true,\"text\":\"O\"},{\"color\":\"#8ce039\",\"bold\":true,\"text\":\"N\"},{\"color\":\"#7cd936\",\"bold\":true,\"text\":\"G\"},{\"color\":\"#6cd233\",\"bold\":true,\"text\":\" \"},{\"color\":\"#5ccb30\",\"bold\":true,\"text\":\"S\"},{\"color\":\"#4cc42d\",\"bold\":true,\"text\":\"L\"},{\"color\":\"#3cbd2a\",\"bold\":true,\"text\":\"I\"},{\"color\":\"#2cb627\",\"bold\":true,\"text\":\"M\"},{\"color\":\"#1caf24\",\"bold\":true,\"text\":\"E\"},{\"color\":\"#0ca821\",\"bold\":true,\"text\":\"S\"},{\"color\":\"#f5e342\",\"text\":\" \"},{\"color\":\"#6d7c87\",\"text\":\"(kinda sus) \"},{\"color\":\"#f5e342\",\"text\":\"✦\"}],\"text\":\"\"}", - " §r§6The §r§c§k||§r§3§lCubeCraft§r§c§k||§r§6 Network §r§a[1.8/1.9+]\n" + - " §r§e✦ §r§d§lN§r§d§lE§r§d§lW§r§d§l:§r§d§l §r§e§lA§r§e§lM§r§a§lO§r§a§lN§r§a§lG§r§a§l §r§a§lS§r§a§lL§r§2§lI§r§2§lM§r§2§lE§r§2§lS§r§e §r§8(kinda sus) §r§e✦"); + " §r§6The §r§d§k||§r§b§lCubeCraft§r§d§k||§r§6 Network §r§a[1.8/1.9+]\n" + + " §r§e✦ §r§d§lN§r§d§lE§r§d§lW§r§d§l:§r§d§l §r§e§lA§r§e§lM§r§e§lO§r§a§lN§r§a§lG§r§a§l §r§a§lS§r§2§lL§r§2§lI§r§2§lM§r§2§lE§r§2§lS§r§e §r§b(kinda sus) §r§e✦"); // Color code format resetting messages.put("{\"text\":\"\",\"extra\":[{\"text\":\"\",\"extra\":[{\"text\":\"[\",\"color\":\"gray\"},{\"text\":\"H\",\"color\":\"yellow\"},{\"text\":\"]\",\"color\":\"gray\"},{\"text\":\" \",\"color\":\"white\"},{\"text\":\"GUEST\",\"color\":\"#b7b7b7\",\"bold\":true}]},{\"text\":\"\",\"extra\":[{\"text\":\" \",\"bold\":true},{\"text\":\"»\",\"color\":\"blue\"},{\"text\":\" \",\"color\":\"gray\"}]},{\"text\":\"\",\"extra\":[{\"text\":\"rtm516\",\"color\":\"white\"},{\"text\":\": \",\"color\":\"gray\"},{\"text\":\"\",\"color\":\"white\"}]},{\"text\":\"\",\"extra\":[{\"text\":\"This is an amazing bedrock test message\",\"color\":\"white\"}]}]}\n", From 1d8961c498229b9e4f480f834a88cdb4a51701cb Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sun, 14 Mar 2021 12:26:47 -0400 Subject: [PATCH 11/19] Allow GeyserWorldManager to be overwritten while still holding a cache (#2036) --- .../manager/GeyserSpigotFallbackWorldManager.java | 2 +- .../spigot/world/manager/GeyserSpigotWorldManager.java | 2 +- .../connector/network/session/cache/ChunkCache.java | 10 +++++----- .../network/translators/world/GeyserWorldManager.java | 2 +- .../network/translators/world/WorldManager.java | 4 ++-- .../java/org/geysermc/connector/utils/ChunkUtils.java | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) 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 4cac791a0..a9de94db5 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 @@ -52,7 +52,7 @@ public class GeyserSpigotFallbackWorldManager extends GeyserSpigotWorldManager { } @Override - public boolean hasMoreBlockDataThanChunkCache() { + public boolean hasOwnChunkCache() { return false; } 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 bc76288c5..ba61eeb72 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 @@ -140,7 +140,7 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager { } @Override - public boolean hasMoreBlockDataThanChunkCache() { + public boolean hasOwnChunkCache() { return true; } diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/ChunkCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/ChunkCache.java index a48b20cee..bf1f4718e 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/cache/ChunkCache.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/ChunkCache.java @@ -29,7 +29,6 @@ import com.github.steveice10.mc.protocol.data.game.chunk.Chunk; import com.github.steveice10.mc.protocol.data.game.chunk.Column; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; -import org.geysermc.connector.bootstrap.GeyserBootstrap; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.world.block.BlockTranslator; import org.geysermc.connector.utils.MathUtils; @@ -38,14 +37,15 @@ public class ChunkCache { private final boolean cache; - private final Long2ObjectMap chunks = new Long2ObjectOpenHashMap<>(); + private final Long2ObjectMap chunks; public ChunkCache(GeyserSession session) { - if (session.getConnector().getWorldManager().getClass() == GeyserBootstrap.DEFAULT_CHUNK_MANAGER.getClass()) { - this.cache = session.getConnector().getConfig().isCacheChunks(); - } else { + if (session.getConnector().getWorldManager().hasOwnChunkCache()) { this.cache = false; // To prevent Spigot from initializing + } else { + this.cache = session.getConnector().getConfig().isCacheChunks(); } + chunks = cache ? new Long2ObjectOpenHashMap<>() : null; } public Column addToCache(Column chunk) { 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 6d2d8720d..014f3e366 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 @@ -73,7 +73,7 @@ public class GeyserWorldManager extends WorldManager { } @Override - public boolean hasMoreBlockDataThanChunkCache() { + public boolean hasOwnChunkCache() { // This implementation can only fetch data from the session chunk cache return false; } 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 6795ae4bf..e97dcec32 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 @@ -88,14 +88,14 @@ public abstract class WorldManager { public abstract void getBlocksInSection(GeyserSession session, int x, int y, int z, Chunk section); /** - * Checks whether or not this world manager has access to more block data than the chunk cache. + * Checks whether or not this world manager requires a separate chunk cache/has access to more block data than the chunk cache. *

* Some world managers (e.g. Spigot) can provide access to block data outside of the chunk cache, and even with chunk caching disabled. This * method provides a means to check if this manager has this capability. * * @return whether or not this world manager has access to more block data than the chunk cache */ - public abstract boolean hasMoreBlockDataThanChunkCache(); + public abstract boolean hasOwnChunkCache(); /** * Gets the Java biome data for the specified chunk. 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 7d5b56615..b6e387237 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java @@ -91,7 +91,7 @@ public class ChunkUtils { BitSet waterloggedPaletteIds = new BitSet(); BitSet pistonOrFlowerPaletteIds = new BitSet(); - boolean worldManagerHasMoreBlockDataThanCache = session.getConnector().getWorldManager().hasMoreBlockDataThanChunkCache(); + boolean worldManagerHasMoreBlockDataThanCache = session.getConnector().getWorldManager().hasOwnChunkCache(); // If the received packet was a full chunk update, null sections in the chunk are guaranteed to also be null in the world manager boolean shouldCheckWorldManagerOnMissingSections = isNonFullChunk && worldManagerHasMoreBlockDataThanCache; From 2f058e05bdf63cb9501c43ad805c66d15f168e01 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sun, 14 Mar 2021 12:52:09 -0400 Subject: [PATCH 12/19] Allow dyed horse leather armor to translate (#2016) --- .../nbt/LeatherArmorTranslator.java | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/LeatherArmorTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/LeatherArmorTranslator.java index f78eadc25..c2305738d 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/LeatherArmorTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/LeatherArmorTranslator.java @@ -35,29 +35,28 @@ import org.geysermc.connector.network.translators.item.ItemEntry; @ItemRemapper public class LeatherArmorTranslator extends NbtItemStackTranslator { - private static final String[] ITEMS = new String[]{"minecraft:leather_helmet", "minecraft:leather_chestplate", "minecraft:leather_leggings", "minecraft:leather_boots"}; + private static final String[] ITEMS = new String[]{"minecraft:leather_helmet", "minecraft:leather_chestplate", + "minecraft:leather_leggings", "minecraft:leather_boots", "minecraft:leather_horse_armor"}; @Override public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemEntry itemEntry) { - if (!itemTag.contains("display")) { + CompoundTag displayTag = itemTag.get("display"); + if (displayTag == null) { return; } - CompoundTag displayTag = itemTag.get("display"); - if (displayTag.contains("color")) { - IntTag color = displayTag.get("color"); - if (color != null) { - itemTag.put(new IntTag("customColor", color.getValue())); - displayTag.remove("color"); - } + IntTag color = displayTag.get("color"); + if (color != null) { + itemTag.put(new IntTag("customColor", color.getValue())); + displayTag.remove("color"); } } @Override public void translateToJava(CompoundTag itemTag, ItemEntry itemEntry) { - if (!itemTag.contains("customColor")) { + IntTag color = itemTag.get("customColor"); + if (color == null) { return; } - IntTag color = itemTag.get("customColor"); CompoundTag displayTag = itemTag.get("display"); if (displayTag == null) { displayTag = new CompoundTag("display"); From d41d8b0ebfb8bf1ec27e3c4319b8a6f8f112955b Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Mon, 15 Mar 2021 14:49:44 -0400 Subject: [PATCH 13/19] Fix item durability 'resetting' on 1.16.210 (#2042) --- .../inventory/InventoryTranslator.java | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 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 5fd44c8b1..4a45b5c9f 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 @@ -32,6 +32,8 @@ 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.opennbt.tag.builtin.IntTag; +import com.github.steveice10.opennbt.tag.builtin.Tag; import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; import com.nukkitx.protocol.bedrock.data.inventory.ItemStackRequest; import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData; @@ -40,7 +42,10 @@ import com.nukkitx.protocol.bedrock.packet.ItemStackResponsePacket; import it.unimi.dsi.fastutil.ints.*; import lombok.AllArgsConstructor; import org.geysermc.connector.GeyserConnector; -import org.geysermc.connector.inventory.*; +import org.geysermc.connector.inventory.CartographyContainer; +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.click.Click; import org.geysermc.connector.network.translators.inventory.click.ClickPlan; @@ -854,7 +859,17 @@ public abstract class InventoryTranslator { public static ItemStackResponsePacket.ItemEntry makeItemEntry(int bedrockSlot, GeyserItemStack itemStack) { ItemStackResponsePacket.ItemEntry itemEntry; if (!itemStack.isEmpty()) { - itemEntry = new ItemStackResponsePacket.ItemEntry((byte) bedrockSlot, (byte) bedrockSlot, (byte) itemStack.getAmount(), itemStack.getNetId(), "", 0); + // As of 1.16.210: Bedrock needs confirmation on what the current item durability is. + // If 0 is sent, then Bedrock thinks the item is not damaged + int durability = 0; + if (itemStack.getNbt() != null) { + Tag damage = itemStack.getNbt().get("Damage"); + if (damage instanceof IntTag) { + durability = ((IntTag) damage).getValue(); + } + } + + itemEntry = new ItemStackResponsePacket.ItemEntry((byte) bedrockSlot, (byte) bedrockSlot, (byte) itemStack.getAmount(), itemStack.getNetId(), "", durability); } else { itemEntry = new ItemStackResponsePacket.ItemEntry((byte) bedrockSlot, (byte) bedrockSlot, (byte) 0, 0, "", 0); } From 3d4fff8dee92635f3e83ea8efe3feb560e9bda10 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 17 Mar 2021 10:38:56 -0400 Subject: [PATCH 14/19] Fix sleeping on older Minecraft server versions (#2049) BED_POSITION is enough to trigger sleep. --- .../main/java/org/geysermc/connector/entity/Entity.java | 3 --- .../java/org/geysermc/connector/entity/LivingEntity.java | 7 +++++++ .../java/entity/JavaEntityMetadataTranslator.java | 4 +++- 3 files changed, 10 insertions(+), 4 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 741f5fcd0..30e63f33c 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/Entity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/Entity.java @@ -331,15 +331,12 @@ public class Entity { case 6: // Pose change if (entityMetadata.getValue().equals(Pose.SLEEPING)) { metadata.getFlags().setFlag(EntityFlag.SLEEPING, true); - // Has to be a byte or it does not work - metadata.put(EntityData.PLAYER_FLAGS, (byte) 2); metadata.put(EntityData.BOUNDING_BOX_WIDTH, 0.2f); metadata.put(EntityData.BOUNDING_BOX_HEIGHT, 0.2f); } else if (metadata.getFlags().getFlag(EntityFlag.SLEEPING)) { metadata.getFlags().setFlag(EntityFlag.SLEEPING, false); metadata.put(EntityData.BOUNDING_BOX_WIDTH, getEntityType().getWidth()); metadata.put(EntityData.BOUNDING_BOX_HEIGHT, getEntityType().getHeight()); - metadata.put(EntityData.PLAYER_FLAGS, (byte) 0); } break; } diff --git a/connector/src/main/java/org/geysermc/connector/entity/LivingEntity.java b/connector/src/main/java/org/geysermc/connector/entity/LivingEntity.java index f38f1e6b2..4dc0998aa 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/LivingEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/LivingEntity.java @@ -99,6 +99,13 @@ public class LivingEntity extends Entity { // Bed has to be updated, or else player is floating in the air ChunkUtils.updateBlock(session, bed, bedPosition); } + // Indicate that the player should enter the sleep cycle + // Has to be a byte or it does not work + // (Bed position is what actually triggers sleep - "pose" is only optional) + metadata.put(EntityData.PLAYER_FLAGS, (byte) 2); + } else { + // Player is no longer sleeping + metadata.put(EntityData.PLAYER_FLAGS, (byte) 0); } break; } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityMetadataTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityMetadataTranslator.java index e3c64d55f..3c4153df3 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityMetadataTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityMetadataTranslator.java @@ -39,9 +39,11 @@ public class JavaEntityMetadataTranslator extends PacketTranslator Date: Wed, 17 Mar 2021 11:15:57 -0400 Subject: [PATCH 15/19] SRV improvements and improvements for GeyserConnect (#2048) - Individual per-player remote, port, and address - Removal of RemoteServer class - Do SRV lookup on startup and that's it --- connector/pom.xml | 4 +- .../geysermc/connector/GeyserConnector.java | 13 +++--- .../network/UpstreamPacketHandler.java | 4 +- .../network/remote/RemoteServer.java | 37 ---------------- .../network/session/GeyserSession.java | 42 ++++++++++++++----- .../geysermc/connector/skin/SkinManager.java | 2 +- 6 files changed, 41 insertions(+), 61 deletions(-) delete mode 100644 connector/src/main/java/org/geysermc/connector/network/remote/RemoteServer.java diff --git a/connector/pom.xml b/connector/pom.xml index cb528e27b..6f2cdab4e 100644 --- a/connector/pom.xml +++ b/connector/pom.xml @@ -140,9 +140,9 @@ - com.github.steveice10 + com.github.GeyserMC PacketLib - 54f761c + b77a427 compile diff --git a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java index 0b7f84646..f0dd696f6 100644 --- a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java +++ b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java @@ -40,7 +40,6 @@ import org.geysermc.connector.common.AuthType; import org.geysermc.connector.configuration.GeyserConfiguration; import org.geysermc.connector.metrics.Metrics; import org.geysermc.connector.network.ConnectorServerEventHandler; -import org.geysermc.connector.network.remote.RemoteServer; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.BiomeTranslator; import org.geysermc.connector.network.translators.EntityIdentifierRegistry; @@ -97,9 +96,8 @@ public class GeyserConnector { private static GeyserConnector instance; - private RemoteServer remoteServer; @Setter - private AuthType authType; + private AuthType defaultAuthType; private boolean shuttingDown = false; @@ -166,7 +164,7 @@ public class GeyserConnector { String remoteAddress = config.getRemote().getAddress(); int remotePort = config.getRemote().getPort(); // Filters whether it is not an IP address or localhost, because otherwise it is not possible to find out an SRV entry. - if ((config.isLegacyPingPassthrough() || platformType == PlatformType.STANDALONE) && !remoteAddress.matches(IP_REGEX) && !remoteAddress.equalsIgnoreCase("localhost")) { + if (!remoteAddress.matches(IP_REGEX) && !remoteAddress.equalsIgnoreCase("localhost")) { try { // Searches for a server address and a port from a SRV record of the specified host name InitialDirContext ctx = new InitialDirContext(); @@ -186,8 +184,7 @@ public class GeyserConnector { } } - remoteServer = new RemoteServer(config.getRemote().getAddress(), remotePort); - authType = AuthType.getByName(config.getRemote().getAuthType()); + defaultAuthType = AuthType.getByName(config.getRemote().getAuthType()); CooldownUtils.setShowCooldown(config.isShowCooldown()); DimensionUtils.changeBedrockNetherId(config.isAboveBedrockNetherBuilding()); // Apply End dimension ID workaround to Nether @@ -334,8 +331,7 @@ public class GeyserConnector { generalThreadPool.shutdown(); bedrockServer.close(); players.clear(); - remoteServer = null; - authType = null; + defaultAuthType = null; this.getCommandManager().getCommands().clear(); bootstrap.getGeyserLogger().info(LanguageUtils.getLocaleStringLog("geyser.core.shutdown.done")); @@ -371,6 +367,7 @@ public class GeyserConnector { * @param xuid the Xbox user identifier * @return the player or null if there is no player online with this xuid */ + @SuppressWarnings("unused") // API usage public GeyserSession getPlayerByXuid(String xuid) { for (GeyserSession session : players) { if (session.getAuthData() != null && session.getAuthData().getXboxUUID().equals(xuid)) { diff --git a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java index 829ae23ef..c85bb773b 100644 --- a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java +++ b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java @@ -97,7 +97,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { public boolean handle(ResourcePackClientResponsePacket packet) { switch (packet.getStatus()) { case COMPLETED: - session.connect(connector.getRemoteServer()); + session.connect(); connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.network.connect", session.getAuthData().getName())); break; @@ -186,7 +186,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { public boolean handle(SetLocalPlayerAsInitializedPacket packet) { LanguageUtils.loadGeyserLocale(session.getLocale()); - if (!session.isLoggedIn() && !session.isLoggingIn() && session.getConnector().getAuthType() == AuthType.ONLINE) { + if (!session.isLoggedIn() && !session.isLoggingIn() && session.getRemoteAuthType() == AuthType.ONLINE) { // TODO it is safer to key authentication on something that won't change (UUID, not username) if (!couldLoginUserByName(session.getAuthData().getName())) { LoginEncryptionUtils.showLoginWindow(session); diff --git a/connector/src/main/java/org/geysermc/connector/network/remote/RemoteServer.java b/connector/src/main/java/org/geysermc/connector/network/remote/RemoteServer.java deleted file mode 100644 index b957b90d6..000000000 --- a/connector/src/main/java/org/geysermc/connector/network/remote/RemoteServer.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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: - * - * 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.remote; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -@Getter -@AllArgsConstructor -public class RemoteServer { - - private String address; - private int port; -} \ No newline at end of file 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 ee3f07f3e..d16cb1c2b 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 @@ -81,7 +81,6 @@ import org.geysermc.connector.entity.player.SessionPlayerEntity; import org.geysermc.connector.entity.player.SkullPlayerEntity; 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; import org.geysermc.connector.network.session.auth.BedrockClientData; import org.geysermc.connector.network.session.cache.*; @@ -113,13 +112,21 @@ public class GeyserSession implements CommandSender { private final GeyserConnector connector; private final UpstreamSession upstream; - private RemoteServer remoteServer; private Client downstream; @Setter private AuthData authData; @Setter private BedrockClientData clientData; + /* Setter for GeyserConnect */ + @Setter + private String remoteAddress; + @Setter + private int remotePort; + @Setter + private AuthType remoteAuthType; + /* Setter for GeyserConnect */ + @Deprecated @Setter private boolean microsoftAccount; @@ -438,9 +445,14 @@ public class GeyserSession implements CommandSender { }); } - public void connect(RemoteServer remoteServer) { + /** + * Send all necessary packets to load Bedrock into the server + */ + public void connect() { startGame(); - this.remoteServer = remoteServer; + this.remoteAddress = connector.getConfig().getRemote().getAddress(); + this.remotePort = connector.getConfig().getRemote().getPort(); + this.remoteAuthType = connector.getDefaultAuthType(); // Set the hardcoded shield ID to the ID we just defined in StartGamePacket upstream.getSession().getHardcodedBlockingId().set(ItemRegistry.SHIELD.getBedrockId()); @@ -485,8 +497,8 @@ public class GeyserSession implements CommandSender { } public void login() { - if (connector.getAuthType() != AuthType.ONLINE) { - if (connector.getAuthType() == AuthType.OFFLINE) { + if (this.remoteAuthType != AuthType.ONLINE) { + if (this.remoteAuthType == AuthType.OFFLINE) { connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.auth.login.offline")); } else { connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.auth.login.floodgate")); @@ -595,7 +607,7 @@ public class GeyserSession implements CommandSender { * After getting whatever credentials needed, we attempt to join the Java server. */ private void connectDownstream() { - boolean floodgate = connector.getAuthType() == AuthType.FLOODGATE; + boolean floodgate = this.remoteAuthType == AuthType.FLOODGATE; final PublicKey publicKey; if (floodgate) { @@ -618,7 +630,8 @@ public class GeyserSession implements CommandSender { // Start ticking tickThread = connector.getGeneralThreadPool().scheduleAtFixedRate(this::tick, 50, 50, TimeUnit.MILLISECONDS); - downstream = new Client(remoteServer.getAddress(), remoteServer.getPort(), protocol, new TcpSessionFactory()); + downstream = new Client(this.remoteAddress, this.remotePort, protocol, new TcpSessionFactory()); + disableSrvResolving(); if (connector.getConfig().getRemote().isUseProxyProtocol()) { downstream.getSession().setFlag(BuiltinFlags.ENABLE_CLIENT_PROXY_PROTOCOL, true); downstream.getSession().setFlag(BuiltinFlags.CLIENT_PROXIED_ADDRESS, upstream.getAddress()); @@ -666,7 +679,7 @@ public class GeyserSession implements CommandSender { disconnect(LanguageUtils.getPlayerLocaleString("geyser.network.remote.invalid_account", clientData.getLanguageCode())); return; } - connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.network.remote.connect", authData.getName(), protocol.getProfile().getName(), remoteServer.getAddress())); + connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.network.remote.connect", authData.getName(), protocol.getProfile().getName(), remoteAddress)); playerEntity.setUuid(protocol.getProfile().getId()); playerEntity.setUsername(protocol.getProfile().getName()); @@ -687,7 +700,7 @@ public class GeyserSession implements CommandSender { public void disconnected(DisconnectedEvent event) { loggingIn = false; loggedIn = false; - connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.network.remote.disconnect", authData.getName(), remoteServer.getAddress(), event.getReason())); + connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.network.remote.disconnect", authData.getName(), remoteAddress, event.getReason())); if (event.getCause() != null) { event.getCause().printStackTrace(); } @@ -705,7 +718,7 @@ public class GeyserSession implements CommandSender { playerEntity.setUuid(profile.getId()); // Check if they are not using a linked account - if (connector.getAuthType() == AuthType.OFFLINE || playerEntity.getUuid().getMostSignificantBits() == 0) { + if (remoteAuthType == AuthType.OFFLINE || playerEntity.getUuid().getMostSignificantBits() == 0) { SkinManager.handleBedrockSkin(playerEntity, clientData); } } @@ -793,6 +806,13 @@ public class GeyserSession implements CommandSender { collisionManager.updateScaffoldingFlags(); } + /** + * Will be overwritten for GeyserConnect. + */ + protected void disableSrvResolving() { + this.downstream.getSession().setFlag(BuiltinFlags.ATTEMPT_SRV_RESOLVE, false); + } + @Override public String getName() { return authData.getName(); diff --git a/connector/src/main/java/org/geysermc/connector/skin/SkinManager.java b/connector/src/main/java/org/geysermc/connector/skin/SkinManager.java index 5a0e41ed5..5af08292a 100644 --- a/connector/src/main/java/org/geysermc/connector/skin/SkinManager.java +++ b/connector/src/main/java/org/geysermc/connector/skin/SkinManager.java @@ -286,7 +286,7 @@ public class SkinManager { String skinUrl = isAlex ? SkinProvider.EMPTY_SKIN_ALEX.getTextureUrl() : SkinProvider.EMPTY_SKIN.getTextureUrl(); String capeUrl = SkinProvider.EMPTY_CAPE.getTextureUrl(); - if (("steve".equals(skinUrl) || "alex".equals(skinUrl)) && GeyserConnector.getInstance().getAuthType() != AuthType.ONLINE) { + if (("steve".equals(skinUrl) || "alex".equals(skinUrl)) && GeyserConnector.getInstance().getDefaultAuthType() != AuthType.ONLINE) { GeyserSession session = GeyserConnector.getInstance().getPlayerByUuid(profile.getId()); if (session != null) { From f827b2885221cdb593331ac453f96863e1b56e2a Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Thu, 18 Mar 2021 00:53:14 -0400 Subject: [PATCH 16/19] More smoothed out shield code (#2051) - Shield code is now down as the player is sneaking - it doesn't wait for the server to update our sneaking status - Shield is now applied correctly when sneaking and then moving to the shield item --- .../org/geysermc/connector/entity/Entity.java | 33 +------------------ ...BedrockInventoryTransactionTranslator.java | 4 +-- .../BedrockMobEquipmentTranslator.java | 20 ++++++++--- .../player/BedrockActionTranslator.java | 33 ++++++++++++++++--- .../geysermc/connector/utils/BlockUtils.java | 10 +++--- 5 files changed, 54 insertions(+), 46 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 30e63f33c..2dcd49fb0 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/Entity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/Entity.java @@ -28,12 +28,6 @@ package org.geysermc.connector.entity; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; import com.github.steveice10.mc.protocol.data.game.entity.metadata.MetadataType; import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose; -import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; -import com.github.steveice10.mc.protocol.data.game.entity.player.Hand; -import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerAction; -import com.github.steveice10.mc.protocol.data.game.world.block.BlockFace; -import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerActionPacket; -import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerUseItemPacket; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.AttributeData; import com.nukkitx.protocol.bedrock.data.entity.EntityData; @@ -50,11 +44,9 @@ import org.geysermc.connector.entity.attribute.AttributeType; import org.geysermc.connector.entity.living.ArmorStandEntity; import org.geysermc.connector.entity.player.PlayerEntity; 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; import org.geysermc.connector.network.translators.chat.MessageTranslator; +import org.geysermc.connector.utils.AttributeUtils; import java.util.ArrayList; import java.util.HashMap; @@ -278,29 +270,6 @@ public class Entity { if (!this.is(ArmorStandEntity.class)) { metadata.getFlags().setFlag(EntityFlag.INVISIBLE, (xd & 0x20) == 0x20); } - - // Shield code - if (session.getPlayerEntity().getEntityId() == entityId && metadata.getFlags().getFlag(EntityFlag.SNEAKING)) { - PlayerInventory playerInv = session.getPlayerInventory(); - if ((playerInv.getItemInHand().getJavaId() == ItemRegistry.SHIELD.getJavaId()) || - (playerInv.getOffhand().getJavaId() == ItemRegistry.SHIELD.getJavaId())) { - ClientPlayerUseItemPacket useItemPacket; - metadata.getFlags().setFlag(EntityFlag.BLOCKING, true); - 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 - else { - useItemPacket = new ClientPlayerUseItemPacket(Hand.OFF_HAND); - } - session.sendDownstreamPacket(useItemPacket); - } - } else if (session.getPlayerEntity().getEntityId() == entityId && !metadata.getFlags().getFlag(EntityFlag.SNEAKING) && metadata.getFlags().getFlag(EntityFlag.BLOCKING)) { - metadata.getFlags().setFlag(EntityFlag.BLOCKING, false); - metadata.getFlags().setFlag(EntityFlag.IS_AVOIDING_BLOCK, true); - ClientPlayerActionPacket releaseItemPacket = new ClientPlayerActionPacket(PlayerAction.RELEASE_USE_ITEM, new Position(0, 0, 0), BlockFace.DOWN); - session.sendDownstreamPacket(releaseItemPacket); - } } break; case 1: // Air/bubbles 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 5258219ba..36c5be44f 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 @@ -94,7 +94,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator 1; ClientPlayerActionPacket dropAllPacket = new ClientPlayerActionPacket( dropAll ? PlayerAction.DROP_ITEM_STACK : PlayerAction.DROP_ITEM, - new Position(0, 0, 0), + BlockUtils.POSITION_ZERO, BlockFace.DOWN ); session.sendDownstreamPacket(dropAllPacket); @@ -292,7 +292,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator { @@ -53,6 +57,14 @@ public class BedrockMobEquipmentTranslator extends PacketTranslator session.sendDownstreamPacket(new ClientPlayerUseItemPacket(Hand.MAIN_HAND)), + 50, TimeUnit.MILLISECONDS); + } + // Java sends a cooldown indicator whenever you switch an item CooldownUtils.sendCooldown(session); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockActionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockActionTranslator.java index ca4250bab..7751fb024 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockActionTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockActionTranslator.java @@ -28,15 +28,13 @@ package org.geysermc.connector.network.translators.bedrock.entity.player; import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; import com.github.steveice10.mc.protocol.data.game.entity.player.*; import com.github.steveice10.mc.protocol.data.game.world.block.BlockFace; -import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerAbilitiesPacket; -import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerActionPacket; -import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerInteractEntityPacket; -import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerStatePacket; +import com.github.steveice10.mc.protocol.packet.ingame.client.player.*; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.nukkitx.math.vector.Vector3i; import com.nukkitx.protocol.bedrock.data.LevelEventType; import com.nukkitx.protocol.bedrock.data.PlayerActionType; import com.nukkitx.protocol.bedrock.data.entity.EntityEventType; +import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.packet.EntityEventPacket; import com.nukkitx.protocol.bedrock.packet.LevelEventPacket; import com.nukkitx.protocol.bedrock.packet.PlayStatusPacket; @@ -49,6 +47,7 @@ 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.item.ItemEntry; +import org.geysermc.connector.network.translators.item.ItemRegistry; import org.geysermc.connector.network.translators.world.block.BlockTranslator; import org.geysermc.connector.utils.BlockUtils; @@ -101,11 +100,37 @@ public class BedrockActionTranslator extends PacketTranslator Date: Thu, 18 Mar 2021 01:11:21 -0400 Subject: [PATCH 17/19] Update the interactive tag when necessary (#2039) Update the interactive tag on entity metadata update, item slot change, and sneaking update; that way, if the tag should change, it will change. --- .../network/session/GeyserSession.java | 11 + .../BedrockMobEquipmentTranslator.java | 6 + .../player/BedrockInteractTranslator.java | 340 +--------------- .../entity/JavaEntityMetadataTranslator.java | 6 + .../utils/InteractiveTagManager.java | 376 ++++++++++++++++++ 5 files changed, 406 insertions(+), 333 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/utils/InteractiveTagManager.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 d16cb1c2b..c7f13398e 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 @@ -264,6 +264,12 @@ public class GeyserSession implements CommandSender { @Setter private Entity ridingVehicleEntity; + /** + * The entity that the client is currently looking at. + */ + @Setter + private Entity mouseoverEntity; + @Setter private Int2ObjectMap craftingRecipes; private final Set unlockedRecipes; @@ -804,6 +810,11 @@ public class GeyserSession implements CommandSender { this.sneaking = sneaking; collisionManager.updatePlayerBoundingBox(); collisionManager.updateScaffoldingFlags(); + + if (mouseoverEntity != null) { + // Horses, etc can change their property depending on if you're sneaking + InteractiveTagManager.updateTag(this, mouseoverEntity); + } } /** 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 134b3dab0..e07f0ae1e 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 @@ -35,6 +35,7 @@ import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; import org.geysermc.connector.network.translators.item.ItemRegistry; import org.geysermc.connector.utils.CooldownUtils; +import org.geysermc.connector.utils.InteractiveTagManager; import java.util.concurrent.TimeUnit; @@ -67,5 +68,10 @@ public class BedrockMobEquipmentTranslator extends PacketTranslator { - /** - * A list of all foods a horse/donkey can eat on Java Edition. - * Used to display interactive tag if needed. - */ - private static final List DONKEY_AND_HORSE_FOODS = Arrays.asList("golden_apple", "enchanted_golden_apple", - "golden_carrot", "sugar", "apple", "wheat", "hay_block"); - - /** - * A list of all flowers. Used for feeding bees. - */ - private static final List FLOWERS = Arrays.asList("dandelion", "poppy", "blue_orchid", "allium", "azure_bluet", - "red_tulip", "pink_tulip", "white_tulip", "orange_tulip", "cornflower", "lily_of_the_valley", "wither_rose", - "sunflower", "lilac", "rose_bush", "peony"); - - /** - * All entity types that can be leashed on Java Edition - */ - private static final List LEASHABLE_MOB_TYPES = Arrays.asList(EntityType.BEE, EntityType.CAT, EntityType.CHICKEN, - EntityType.COW, EntityType.DOLPHIN, EntityType.DONKEY, EntityType.FOX, EntityType.HOGLIN, EntityType.HORSE, EntityType.SKELETON_HORSE, - EntityType.ZOMBIE_HORSE, EntityType.IRON_GOLEM, EntityType.LLAMA, EntityType.TRADER_LLAMA, EntityType.MOOSHROOM, - EntityType.MULE, EntityType.OCELOT, EntityType.PARROT, EntityType.PIG, EntityType.POLAR_BEAR, EntityType.RABBIT, - EntityType.SHEEP, EntityType.SNOW_GOLEM, EntityType.STRIDER, EntityType.WOLF, EntityType.ZOGLIN); - - private static final List SADDLEABLE_WHEN_TAMED_MOB_TYPES = Arrays.asList(EntityType.DONKEY, EntityType.HORSE, - EntityType.ZOMBIE_HORSE, EntityType.MULE); - /** - * A list of all foods a wolf can eat on Java Edition. - * Used to display interactive tag if needed. - */ - private static final List WOLF_FOODS = Arrays.asList("pufferfish", "tropical_fish", "chicken", "cooked_chicken", - "porkchop", "beef", "rabbit", "cooked_porkchop", "cooked_beef", "rotten_flesh", "mutton", "cooked_mutton", - "cooked_rabbit"); - @Override public void translate(InteractPacket packet, GeyserSession session) { Entity entity; @@ -121,241 +81,16 @@ public class BedrockInteractTranslator extends PacketTranslator // Handle the buttons for mobile - "Mount", etc; and the suggestions for console - "ZL: Mount", etc if (packet.getRuntimeEntityId() != 0) { Entity interactEntity = session.getEntityCache().getEntityByGeyserId(packet.getRuntimeEntityId()); - if (interactEntity == null) + session.setMouseoverEntity(interactEntity); + if (interactEntity == null) { return; - EntityDataMap entityMetadata = interactEntity.getMetadata(); - ItemEntry itemEntry = session.getPlayerInventory().getItemInHand().getItemEntry(); - String javaIdentifierStripped = itemEntry.getJavaIdentifier().replace("minecraft:", ""); - - // TODO - in the future, update these in the metadata? So the client doesn't have to wiggle their cursor around for it to happen - // TODO - also, might be good to abstract out the eating thing. I know there will need to be food tracked for https://github.com/GeyserMC/Geyser/issues/1005 but not all food is breeding food - InteractiveTag interactiveTag = InteractiveTag.NONE; - if (entityMetadata.getLong(EntityData.LEASH_HOLDER_EID) == session.getPlayerEntity().getGeyserId()) { - // Unleash the entity - interactiveTag = InteractiveTag.REMOVE_LEASH; - } else if (javaIdentifierStripped.equals("saddle") && !entityMetadata.getFlags().getFlag(EntityFlag.SADDLED) && - ((SADDLEABLE_WHEN_TAMED_MOB_TYPES.contains(interactEntity.getEntityType()) && entityMetadata.getFlags().getFlag(EntityFlag.TAMED)) || - interactEntity.getEntityType() == EntityType.PIG || interactEntity.getEntityType() == EntityType.STRIDER)) { - // Entity can be saddled and the conditions meet (entity can be saddled and, if needed, is tamed) - interactiveTag = InteractiveTag.SADDLE; - } else if (javaIdentifierStripped.equals("name_tag") && session.getPlayerInventory().getItemInHand().getNbt() != null && - session.getPlayerInventory().getItemInHand().getNbt().contains("display")) { - // Holding a named name tag - interactiveTag = InteractiveTag.NAME; - } else if (javaIdentifierStripped.equals("lead") && LEASHABLE_MOB_TYPES.contains(interactEntity.getEntityType()) && - entityMetadata.getLong(EntityData.LEASH_HOLDER_EID) == -1L) { - // Holding a leash and the mob is leashable for sure - // (Plugins can change this behavior so that's something to look into in the far far future) - interactiveTag = InteractiveTag.LEASH; - } else { - switch (interactEntity.getEntityType()) { - case BEE: - if (FLOWERS.contains(javaIdentifierStripped)) { - interactiveTag = InteractiveTag.FEED; - } - break; - case BOAT: - interactiveTag = InteractiveTag.BOARD_BOAT; - break; - case CAT: - if (javaIdentifierStripped.equals("cod") || javaIdentifierStripped.equals("salmon")) { - interactiveTag = InteractiveTag.FEED; - } else if (entityMetadata.getFlags().getFlag(EntityFlag.TAMED) && - entityMetadata.getLong(EntityData.OWNER_EID) == session.getPlayerEntity().getGeyserId()) { - // Tamed and owned by player - can sit/stand - interactiveTag = entityMetadata.getFlags().getFlag(EntityFlag.SITTING) ? InteractiveTag.STAND : InteractiveTag.SIT; - break; - } - break; - case CHICKEN: - if (javaIdentifierStripped.contains("seeds")) { - interactiveTag = InteractiveTag.FEED; - } - break; - case MOOSHROOM: - // Shear the mooshroom - if (javaIdentifierStripped.equals("shears")) { - interactiveTag = InteractiveTag.MOOSHROOM_SHEAR; - break; - } - // Bowls are acceptable here - else if (javaIdentifierStripped.equals("bowl")) { - interactiveTag = InteractiveTag.MOOSHROOM_MILK_STEW; - break; - } - // Fall down to COW as this works on mooshrooms - case COW: - if (javaIdentifierStripped.equals("wheat")) { - interactiveTag = InteractiveTag.FEED; - } else if (javaIdentifierStripped.equals("bucket")) { - // Milk the cow - interactiveTag = InteractiveTag.MILK; - } - break; - case CREEPER: - if (javaIdentifierStripped.equals("flint_and_steel")) { - // Today I learned that you can ignite a creeper with flint and steel! Huh. - interactiveTag = InteractiveTag.IGNITE_CREEPER; - } - break; - case DONKEY: - case LLAMA: - case MULE: - if (entityMetadata.getFlags().getFlag(EntityFlag.TAMED) && !entityMetadata.getFlags().getFlag(EntityFlag.CHESTED) - && javaIdentifierStripped.equals("chest")) { - // Can attach a chest - interactiveTag = InteractiveTag.ATTACH_CHEST; - break; - } - // Intentional fall-through - case HORSE: - 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: - case TRADER_LLAMA: - if (javaIdentifierStripped.equals("wheat") || javaIdentifierStripped.equals("hay_block")) { - interactiveTag = InteractiveTag.FEED; - break; - } - case DONKEY: - case HORSE: - // Undead can't eat - if (DONKEY_AND_HORSE_FOODS.contains(javaIdentifierStripped)) { - interactiveTag = InteractiveTag.FEED; - break; - } - } - if (!entityMetadata.getFlags().getFlag(EntityFlag.BABY)) { - // Can't ride a baby - if (tamed) { - interactiveTag = InteractiveTag.RIDE_HORSE; - } else if (itemEntry.equals(ItemEntry.AIR)) { - // Can't hide an untamed entity without having your hand empty - interactiveTag = InteractiveTag.MOUNT; - } - } - break; - case FOX: - if (javaIdentifierStripped.equals("sweet_berries")) { - interactiveTag = InteractiveTag.FEED; - } - break; - case HOGLIN: - if (javaIdentifierStripped.equals("crimson_fungus")) { - interactiveTag = InteractiveTag.FEED; - } - break; - case MINECART: - interactiveTag = InteractiveTag.RIDE_MINECART; - break; - case MINECART_CHEST: - case MINECART_COMMAND_BLOCK: - case MINECART_HOPPER: - interactiveTag = InteractiveTag.OPEN_CONTAINER; - break; - case OCELOT: - if (javaIdentifierStripped.equals("cod") || javaIdentifierStripped.equals("salmon")) { - interactiveTag = InteractiveTag.FEED; - } - break; - case PANDA: - if (javaIdentifierStripped.equals("bamboo")) { - interactiveTag = InteractiveTag.FEED; - } - break; - case PARROT: - if (javaIdentifierStripped.contains("seeds") || javaIdentifierStripped.equals("cookie")) { - interactiveTag = InteractiveTag.FEED; - } - break; - case PIG: - if (javaIdentifierStripped.equals("carrot") || javaIdentifierStripped.equals("potato") || javaIdentifierStripped.equals("beetroot")) { - interactiveTag = InteractiveTag.FEED; - } else if (entityMetadata.getFlags().getFlag(EntityFlag.SADDLED)) { - interactiveTag = InteractiveTag.MOUNT; - } - break; - case PIGLIN: - if (!entityMetadata.getFlags().getFlag(EntityFlag.BABY) && javaIdentifierStripped.equals("gold_ingot")) { - interactiveTag = InteractiveTag.BARTER; - } - break; - case RABBIT: - if (javaIdentifierStripped.equals("dandelion") || javaIdentifierStripped.equals("carrot") || javaIdentifierStripped.equals("golden_carrot")) { - interactiveTag = InteractiveTag.FEED; - } - break; - case SHEEP: - if (javaIdentifierStripped.equals("wheat")) { - interactiveTag = InteractiveTag.FEED; - } else if (!entityMetadata.getFlags().getFlag(EntityFlag.SHEARED)) { - if (javaIdentifierStripped.equals("shears")) { - // Shear the sheep - interactiveTag = InteractiveTag.SHEAR; - } else if (javaIdentifierStripped.contains("_dye")) { - // Dye the sheep - interactiveTag = InteractiveTag.DYE; - } - } - break; - case STRIDER: - if (javaIdentifierStripped.equals("warped_fungus")) { - interactiveTag = InteractiveTag.FEED; - } else if (entityMetadata.getFlags().getFlag(EntityFlag.SADDLED)) { - interactiveTag = InteractiveTag.RIDE_STRIDER; - } - break; - case TURTLE: - if (javaIdentifierStripped.equals("seagrass")) { - interactiveTag = InteractiveTag.FEED; - } - break; - case VILLAGER: - if (entityMetadata.getInt(EntityData.VARIANT) != 14 && entityMetadata.getInt(EntityData.VARIANT) != 0 - && entityMetadata.getFloat(EntityData.SCALE) >= 0.75f) { // Not a nitwit, has a profession and is not a baby - interactiveTag = InteractiveTag.TRADE; - } - break; - case WANDERING_TRADER: - interactiveTag = InteractiveTag.TRADE; // Since you can always trade with a wandering villager, presumably. - break; - case WOLF: - if (javaIdentifierStripped.equals("bone") && !entityMetadata.getFlags().getFlag(EntityFlag.TAMED)) { - // Bone and untamed - can tame - interactiveTag = InteractiveTag.TAME; - } else if (WOLF_FOODS.contains(javaIdentifierStripped)) { - // Compatible food in hand - feed - // Sometimes just sits/stands when the wolf isn't hungry - there doesn't appear to be a way to fix this - interactiveTag = InteractiveTag.FEED; - } else if (entityMetadata.getFlags().getFlag(EntityFlag.TAMED) && - entityMetadata.getLong(EntityData.OWNER_EID) == session.getPlayerEntity().getGeyserId()) { - // Tamed and owned by player - can sit/stand - interactiveTag = entityMetadata.getFlags().getFlag(EntityFlag.SITTING) ? InteractiveTag.STAND : InteractiveTag.SIT; - } - break; - case ZOMBIE_VILLAGER: - // We can't guarantee the existence of the weakness effect so we just always show it. - if (javaIdentifierStripped.equals("golden_apple")) { - interactiveTag = InteractiveTag.CURE; - } - break; - default: - break; - } } - session.getPlayerEntity().getMetadata().put(EntityData.INTERACTIVE_TAG, interactiveTag.getValue()); - session.getPlayerEntity().updateBedrockMetadata(session); + + InteractiveTagManager.updateTag(session, interactEntity); } else { - if (!session.getPlayerEntity().getMetadata().getString(EntityData.INTERACTIVE_TAG).isEmpty()) { + if (session.getMouseoverEntity() != null) { // No interactive tag should be sent + session.setMouseoverEntity(null); session.getPlayerEntity().getMetadata().put(EntityData.INTERACTIVE_TAG, ""); session.getPlayerEntity().updateBedrockMetadata(session); } @@ -384,65 +119,4 @@ public class BedrockInteractTranslator extends PacketTranslator break; } } - - /** - * All interactive tags in enum form. For potential API usage. - */ - public enum InteractiveTag { - NONE(true), - IGNITE_CREEPER("creeper"), - EDIT, - LEAVE_BOAT("exit.boat"), - FEED, - FISH("fishing"), - MILK, - MOOSHROOM_SHEAR("mooshear"), - MOOSHROOM_MILK_STEW("moostew"), - BOARD_BOAT("ride.boat"), - RIDE_MINECART("ride.minecart"), - RIDE_HORSE("ride.horse"), - RIDE_STRIDER("ride.strider"), - SHEAR, - SIT, - STAND, - TALK, - TAME, - DYE, - CURE, - OPEN_CONTAINER("opencontainer"), - CREATE_MAP("createMap"), - TAKE_PICTURE("takepicture"), - SADDLE, - MOUNT, - BOOST, - WRITE, - LEASH, - REMOVE_LEASH("unleash"), - NAME, - ATTACH_CHEST("attachchest"), - TRADE, - POSE_ARMOR_STAND("armorstand.pose"), - EQUIP_ARMOR_STAND("armorstand.equip"), - READ, - WAKE_VILLAGER("wakevillager"), - BARTER; - - /** - * The full string that should be passed on to the client. - */ - @Getter - private final String value; - - InteractiveTag(boolean isNone) { - this.value = ""; - } - - InteractiveTag(String value) { - this.value = "action.interact." + value; - } - - InteractiveTag() { - this.value = "action.interact." + name().toLowerCase(); - } - } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityMetadataTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityMetadataTranslator.java index 3c4153df3..73047d0c4 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityMetadataTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityMetadataTranslator.java @@ -32,6 +32,7 @@ import org.geysermc.connector.network.translators.Translator; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; import com.github.steveice10.mc.protocol.packet.ingame.server.entity.ServerEntityMetadataPacket; +import org.geysermc.connector.utils.InteractiveTagManager; import org.geysermc.connector.utils.LanguageUtils; @Translator(packet = ServerEntityMetadataPacket.class) @@ -63,5 +64,10 @@ public class JavaEntityMetadataTranslator extends PacketTranslator DONKEY_AND_HORSE_FOODS = ImmutableSet.of("golden_apple", "enchanted_golden_apple", + "golden_carrot", "sugar", "apple", "wheat", "hay_block"); + + /** + * A list of all flowers. Used for feeding bees. + */ + private static final Set FLOWERS = ImmutableSet.of("dandelion", "poppy", "blue_orchid", "allium", "azure_bluet", + "red_tulip", "pink_tulip", "white_tulip", "orange_tulip", "cornflower", "lily_of_the_valley", "wither_rose", + "sunflower", "lilac", "rose_bush", "peony"); + + /** + * All entity types that can be leashed on Java Edition + */ + private static final Set LEASHABLE_MOB_TYPES = EnumSet.of(EntityType.BEE, EntityType.CAT, EntityType.CHICKEN, + EntityType.COW, EntityType.DOLPHIN, EntityType.DONKEY, EntityType.FOX, EntityType.HOGLIN, EntityType.HORSE, EntityType.SKELETON_HORSE, + EntityType.ZOMBIE_HORSE, EntityType.IRON_GOLEM, EntityType.LLAMA, EntityType.TRADER_LLAMA, EntityType.MOOSHROOM, + EntityType.MULE, EntityType.OCELOT, EntityType.PARROT, EntityType.PIG, EntityType.POLAR_BEAR, EntityType.RABBIT, + EntityType.SHEEP, EntityType.SNOW_GOLEM, EntityType.STRIDER, EntityType.WOLF, EntityType.ZOGLIN); + + private static final Set SADDLEABLE_WHEN_TAMED_MOB_TYPES = EnumSet.of(EntityType.DONKEY, EntityType.HORSE, + EntityType.ZOMBIE_HORSE, EntityType.MULE); + + /** + * A list of all foods a wolf can eat on Java Edition. + * Used to display interactive tag if needed. + */ + private static final Set WOLF_FOODS = ImmutableSet.of("pufferfish", "tropical_fish", "chicken", "cooked_chicken", + "porkchop", "beef", "rabbit", "cooked_porkchop", "cooked_beef", "rotten_flesh", "mutton", "cooked_mutton", + "cooked_rabbit"); + + /** + * Update the suggestion that the client currently has on their screen for this entity (for example, "Feed" or "Ride") + * + * @param session the Bedrock client session + * @param interactEntity the entity that the client is currently facing. + */ + public static void updateTag(GeyserSession session, Entity interactEntity) { + EntityDataMap entityMetadata = interactEntity.getMetadata(); + ItemEntry itemEntry = session.getPlayerInventory().getItemInHand().getItemEntry(); + String javaIdentifierStripped = itemEntry.getJavaIdentifier().replace("minecraft:", ""); + + // TODO - in the future, update these in the metadata? So the client doesn't have to wiggle their cursor around for it to happen + // TODO - also, might be good to abstract out the eating thing. I know there will need to be food tracked for https://github.com/GeyserMC/Geyser/issues/1005 but not all food is breeding food + InteractiveTag interactiveTag = InteractiveTag.NONE; + if (entityMetadata.getLong(EntityData.LEASH_HOLDER_EID) == session.getPlayerEntity().getGeyserId()) { + // Unleash the entity + interactiveTag = InteractiveTag.REMOVE_LEASH; + } else if (javaIdentifierStripped.equals("saddle") && !entityMetadata.getFlags().getFlag(EntityFlag.SADDLED) && + ((SADDLEABLE_WHEN_TAMED_MOB_TYPES.contains(interactEntity.getEntityType()) && entityMetadata.getFlags().getFlag(EntityFlag.TAMED) && !session.isSneaking()) || + interactEntity.getEntityType() == EntityType.PIG || interactEntity.getEntityType() == EntityType.STRIDER)) { + // Entity can be saddled and the conditions meet (entity can be saddled and, if needed, is tamed) + interactiveTag = InteractiveTag.SADDLE; + } else if (javaIdentifierStripped.equals("name_tag") && session.getPlayerInventory().getItemInHand().getNbt() != null && + session.getPlayerInventory().getItemInHand().getNbt().contains("display")) { + // Holding a named name tag + interactiveTag = InteractiveTag.NAME; + } else if (javaIdentifierStripped.equals("lead") && LEASHABLE_MOB_TYPES.contains(interactEntity.getEntityType()) && + entityMetadata.getLong(EntityData.LEASH_HOLDER_EID, -1L) == -1L) { + // Holding a leash and the mob is leashable for sure + // (Plugins can change this behavior so that's something to look into in the far far future) + interactiveTag = InteractiveTag.LEASH; + } else { + switch (interactEntity.getEntityType()) { + case BEE: + if (FLOWERS.contains(javaIdentifierStripped)) { + interactiveTag = InteractiveTag.FEED; + } + break; + case BOAT: + interactiveTag = InteractiveTag.BOARD_BOAT; + break; + case CAT: + if (javaIdentifierStripped.equals("cod") || javaIdentifierStripped.equals("salmon")) { + interactiveTag = InteractiveTag.FEED; + } else if (entityMetadata.getFlags().getFlag(EntityFlag.TAMED) && + entityMetadata.getLong(EntityData.OWNER_EID) == session.getPlayerEntity().getGeyserId()) { + // Tamed and owned by player - can sit/stand + interactiveTag = entityMetadata.getFlags().getFlag(EntityFlag.SITTING) ? InteractiveTag.STAND : InteractiveTag.SIT; + break; + } + break; + case CHICKEN: + if (javaIdentifierStripped.contains("seeds")) { + interactiveTag = InteractiveTag.FEED; + } + break; + case MOOSHROOM: + // Shear the mooshroom + if (javaIdentifierStripped.equals("shears")) { + interactiveTag = InteractiveTag.MOOSHROOM_SHEAR; + break; + } + // Bowls are acceptable here + else if (javaIdentifierStripped.equals("bowl")) { + interactiveTag = InteractiveTag.MOOSHROOM_MILK_STEW; + break; + } + // Fall down to COW as this works on mooshrooms + case COW: + if (javaIdentifierStripped.equals("wheat")) { + interactiveTag = InteractiveTag.FEED; + } else if (javaIdentifierStripped.equals("bucket")) { + // Milk the cow + interactiveTag = InteractiveTag.MILK; + } + break; + case CREEPER: + if (javaIdentifierStripped.equals("flint_and_steel")) { + // Today I learned that you can ignite a creeper with flint and steel! Huh. + interactiveTag = InteractiveTag.IGNITE_CREEPER; + } + break; + case DONKEY: + case LLAMA: + case MULE: + if (entityMetadata.getFlags().getFlag(EntityFlag.TAMED) && !entityMetadata.getFlags().getFlag(EntityFlag.CHESTED) + && javaIdentifierStripped.equals("chest")) { + // Can attach a chest + interactiveTag = InteractiveTag.ATTACH_CHEST; + break; + } + // Intentional fall-through + case HORSE: + 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: + case TRADER_LLAMA: + if (javaIdentifierStripped.equals("wheat") || javaIdentifierStripped.equals("hay_block")) { + interactiveTag = InteractiveTag.FEED; + break; + } + case DONKEY: + case HORSE: + // Undead can't eat + if (DONKEY_AND_HORSE_FOODS.contains(javaIdentifierStripped)) { + interactiveTag = InteractiveTag.FEED; + break; + } + } + if (!entityMetadata.getFlags().getFlag(EntityFlag.BABY)) { + // Can't ride a baby + if (tamed) { + interactiveTag = InteractiveTag.RIDE_HORSE; + } else if (itemEntry.equals(ItemEntry.AIR)) { + // Can't hide an untamed entity without having your hand empty + interactiveTag = InteractiveTag.MOUNT; + } + } + break; + case FOX: + if (javaIdentifierStripped.equals("sweet_berries")) { + interactiveTag = InteractiveTag.FEED; + } + break; + case HOGLIN: + if (javaIdentifierStripped.equals("crimson_fungus")) { + interactiveTag = InteractiveTag.FEED; + } + break; + case MINECART: + interactiveTag = InteractiveTag.RIDE_MINECART; + break; + case MINECART_CHEST: + case MINECART_COMMAND_BLOCK: + case MINECART_HOPPER: + interactiveTag = InteractiveTag.OPEN_CONTAINER; + break; + case OCELOT: + if (javaIdentifierStripped.equals("cod") || javaIdentifierStripped.equals("salmon")) { + interactiveTag = InteractiveTag.FEED; + } + break; + case PANDA: + if (javaIdentifierStripped.equals("bamboo")) { + interactiveTag = InteractiveTag.FEED; + } + break; + case PARROT: + if (javaIdentifierStripped.contains("seeds") || javaIdentifierStripped.equals("cookie")) { + interactiveTag = InteractiveTag.FEED; + } + break; + case PIG: + if (javaIdentifierStripped.equals("carrot") || javaIdentifierStripped.equals("potato") || javaIdentifierStripped.equals("beetroot")) { + interactiveTag = InteractiveTag.FEED; + } else if (entityMetadata.getFlags().getFlag(EntityFlag.SADDLED)) { + interactiveTag = InteractiveTag.MOUNT; + } + break; + case PIGLIN: + if (!entityMetadata.getFlags().getFlag(EntityFlag.BABY) && javaIdentifierStripped.equals("gold_ingot")) { + interactiveTag = InteractiveTag.BARTER; + } + break; + case RABBIT: + if (javaIdentifierStripped.equals("dandelion") || javaIdentifierStripped.equals("carrot") || javaIdentifierStripped.equals("golden_carrot")) { + interactiveTag = InteractiveTag.FEED; + } + break; + case SHEEP: + if (javaIdentifierStripped.equals("wheat")) { + interactiveTag = InteractiveTag.FEED; + } else if (!entityMetadata.getFlags().getFlag(EntityFlag.SHEARED)) { + if (javaIdentifierStripped.equals("shears")) { + // Shear the sheep + interactiveTag = InteractiveTag.SHEAR; + } else if (javaIdentifierStripped.contains("_dye")) { + // Dye the sheep + interactiveTag = InteractiveTag.DYE; + } + } + break; + case STRIDER: + if (javaIdentifierStripped.equals("warped_fungus")) { + interactiveTag = InteractiveTag.FEED; + } else if (entityMetadata.getFlags().getFlag(EntityFlag.SADDLED)) { + interactiveTag = InteractiveTag.RIDE_STRIDER; + } + break; + case TURTLE: + if (javaIdentifierStripped.equals("seagrass")) { + interactiveTag = InteractiveTag.FEED; + } + break; + case VILLAGER: + if (entityMetadata.getInt(EntityData.VARIANT) != 14 && entityMetadata.getInt(EntityData.VARIANT) != 0 + && entityMetadata.getFloat(EntityData.SCALE) >= 0.75f) { // Not a nitwit, has a profession and is not a baby + interactiveTag = InteractiveTag.TRADE; + } + break; + case WANDERING_TRADER: + interactiveTag = InteractiveTag.TRADE; // Since you can always trade with a wandering villager, presumably. + break; + case WOLF: + if (javaIdentifierStripped.equals("bone") && !entityMetadata.getFlags().getFlag(EntityFlag.TAMED)) { + // Bone and untamed - can tame + interactiveTag = InteractiveTag.TAME; + } else if (WOLF_FOODS.contains(javaIdentifierStripped)) { + // Compatible food in hand - feed + // Sometimes just sits/stands when the wolf isn't hungry - there doesn't appear to be a way to fix this + interactiveTag = InteractiveTag.FEED; + } else if (entityMetadata.getFlags().getFlag(EntityFlag.TAMED) && + entityMetadata.getLong(EntityData.OWNER_EID) == session.getPlayerEntity().getGeyserId()) { + // Tamed and owned by player - can sit/stand + interactiveTag = entityMetadata.getFlags().getFlag(EntityFlag.SITTING) ? InteractiveTag.STAND : InteractiveTag.SIT; + } + break; + case ZOMBIE_VILLAGER: + // We can't guarantee the existence of the weakness effect so we just always show it. + if (javaIdentifierStripped.equals("golden_apple")) { + interactiveTag = InteractiveTag.CURE; + } + break; + default: + break; + } + } + session.getPlayerEntity().getMetadata().put(EntityData.INTERACTIVE_TAG, interactiveTag.getValue()); + session.getPlayerEntity().updateBedrockMetadata(session); + } + + /** + * All interactive tags in enum form. For potential API usage. + */ + public enum InteractiveTag { + NONE(true), + IGNITE_CREEPER("creeper"), + EDIT, + LEAVE_BOAT("exit.boat"), + FEED, + FISH("fishing"), + MILK, + MOOSHROOM_SHEAR("mooshear"), + MOOSHROOM_MILK_STEW("moostew"), + BOARD_BOAT("ride.boat"), + RIDE_MINECART("ride.minecart"), + RIDE_HORSE("ride.horse"), + RIDE_STRIDER("ride.strider"), + SHEAR, + SIT, + STAND, + TALK, + TAME, + DYE, + CURE, + OPEN_CONTAINER("opencontainer"), + CREATE_MAP("createMap"), + TAKE_PICTURE("takepicture"), + SADDLE, + MOUNT, + BOOST, + WRITE, + LEASH, + REMOVE_LEASH("unleash"), + NAME, + ATTACH_CHEST("attachchest"), + TRADE, + POSE_ARMOR_STAND("armorstand.pose"), + EQUIP_ARMOR_STAND("armorstand.equip"), + READ, + WAKE_VILLAGER("wakevillager"), + BARTER; + + /** + * The full string that should be passed on to the client. + */ + @Getter + private final String value; + + InteractiveTag(boolean isNone) { + this.value = ""; + } + + InteractiveTag(String value) { + this.value = "action.interact." + value; + } + + InteractiveTag() { + this.value = "action.interact." + name().toLowerCase(); + } + } +} From 10c77a3214ffb03c93a5b4401c89be36150b3c8d Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sun, 21 Mar 2021 11:28:19 -0400 Subject: [PATCH 18/19] Fix sign color changes messing with character count (#2054) Color changes do not affect the maximum amount of characters allowed on a line. --- .../entity/SignBlockEntityTranslator.java | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) 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 a92fac599..61ca4fa9c 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 @@ -26,6 +26,7 @@ package org.geysermc.connector.network.translators.world.block.entity; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import com.github.steveice10.opennbt.tag.builtin.Tag; import com.nukkitx.nbt.NbtMapBuilder; import org.geysermc.connector.network.translators.chat.MessageTranslator; import org.geysermc.connector.utils.SignUtils; @@ -35,7 +36,7 @@ public class SignBlockEntityTranslator extends BlockEntityTranslator { /** * Maps a color stored in a sign's Color tag to a Bedrock Edition formatting code. *
- * The color names correspond to dye names, because of this we can't use {@link MessageTranslator#getColor(String)}. + * The color names correspond to dye names, because of this we can't use a more global method. * * @param javaColor The dye color stored in the sign's Color tag. * @return A Bedrock Edition formatting code for valid dye colors, otherwise an empty string. @@ -101,27 +102,34 @@ public class SignBlockEntityTranslator extends BlockEntityTranslator { String signLine = getOrDefault(tag.getValue().get("Text" + currentLine), ""); signLine = MessageTranslator.convertMessageLenient(signLine); - // Trim any trailing formatting codes - if (signLine.length() > 2 && signLine.toCharArray()[signLine.length() - 2] == '\u00a7') { - signLine = signLine.substring(0, signLine.length() - 2); - } - // Check the character width on the sign to ensure there is no overflow that is usually hidden // to Java Edition clients but will appear to Bedrock clients int signWidth = 0; StringBuilder finalSignLine = new StringBuilder(); + boolean previousCharacterWasFormatting = false; // Color changes do not count for maximum width for (char c : signLine.toCharArray()) { - signWidth += SignUtils.getCharacterWidth(c); + if (c == '\u00a7') { + // Don't count this character + previousCharacterWasFormatting = true; + } else if (previousCharacterWasFormatting) { + // Don't count this character either + previousCharacterWasFormatting = false; + } else { + signWidth += SignUtils.getCharacterWidth(c); + } + if (signWidth <= SignUtils.BEDROCK_CHARACTER_WIDTH_MAX) { finalSignLine.append(c); } else { + // Adding the character would make Bedrock move to the next line - Java doesn't do that, so we do not want to break; } } // Java Edition 1.14 added the ability to change the text color of the whole sign using dye - if (tag.contains("Color")) { - signText.append(getBedrockSignColor(tag.get("Color").getValue().toString())); + Tag color = tag.get("Color"); + if (color != null) { + signText.append(getBedrockSignColor(color.getValue().toString())); } signText.append(finalSignLine.toString()); From 86397ef4836e2e716f910d210a1e5ebd8fdeec57 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sun, 21 Mar 2021 11:31:09 -0400 Subject: [PATCH 19/19] Chunk cache improvements (#2053) - Check for minimum and maximum Y values to fix stack traces - Don't run half of the villager sleeping code unless they're actually sleeping --- .../connector/entity/FishingHookEntity.java | 42 ++++++------ .../living/merchant/VillagerEntity.java | 65 +++++++++---------- .../network/session/cache/ChunkCache.java | 11 ++++ 3 files changed, 62 insertions(+), 56 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/entity/FishingHookEntity.java b/connector/src/main/java/org/geysermc/connector/entity/FishingHookEntity.java index 06e56ad03..0738c3819 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/FishingHookEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/FishingHookEntity.java @@ -96,27 +96,25 @@ public class FishingHookEntity extends ThrowableEntity { boolean touchingWater = false; boolean collided = false; for (Vector3i blockPos : collidableBlocks) { - if (0 <= blockPos.getY() && blockPos.getY() <= 255) { - int blockID = session.getConnector().getWorldManager().getBlockAt(session, blockPos); - BlockCollision blockCollision = CollisionTranslator.getCollision(blockID, blockPos.getX(), blockPos.getY(), blockPos.getZ()); - if (blockCollision != null && blockCollision.checkIntersection(boundingBox)) { - // TODO Push bounding box out of collision to improve movement - collided = true; - } + int blockID = session.getConnector().getWorldManager().getBlockAt(session, blockPos); + BlockCollision blockCollision = CollisionTranslator.getCollision(blockID, blockPos.getX(), blockPos.getY(), blockPos.getZ()); + if (blockCollision != null && blockCollision.checkIntersection(boundingBox)) { + // TODO Push bounding box out of collision to improve movement + collided = true; + } - int waterLevel = BlockStateValues.getWaterLevel(blockID); - if (BlockTranslator.isWaterlogged(blockID)) { - waterLevel = 0; + int waterLevel = BlockStateValues.getWaterLevel(blockID); + if (BlockTranslator.isWaterlogged(blockID)) { + waterLevel = 0; + } + if (waterLevel >= 0) { + double waterMaxY = blockPos.getY() + 1 - (waterLevel + 1) / 9.0; + // Falling water is a full block + if (waterLevel >= 8) { + waterMaxY = blockPos.getY() + 1; } - if (waterLevel >= 0) { - double waterMaxY = blockPos.getY() + 1 - (waterLevel + 1) / 9.0; - // Falling water is a full block - if (waterLevel >= 8) { - waterMaxY = blockPos.getY() + 1; - } - if (position.getY() <= waterMaxY) { - touchingWater = true; - } + if (position.getY() <= waterMaxY) { + touchingWater = true; } } } @@ -177,10 +175,8 @@ public class FishingHookEntity extends ThrowableEntity { */ protected boolean isInAir(GeyserSession session) { if (session.getConnector().getConfig().isCacheChunks()) { - if (0 <= position.getFloorY() && position.getFloorY() <= 255) { - int block = session.getConnector().getWorldManager().getBlockAt(session, position.toInt()); - return block == BlockTranslator.JAVA_AIR_ID; - } + int block = session.getConnector().getWorldManager().getBlockAt(session, position.toInt()); + return block == BlockTranslator.JAVA_AIR_ID; } return false; } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/merchant/VillagerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/merchant/VillagerEntity.java index 56354774d..fa5785fe5 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/merchant/VillagerEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/merchant/VillagerEntity.java @@ -26,7 +26,6 @@ package org.geysermc.connector.entity.living.merchant; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; -import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; import com.github.steveice10.mc.protocol.data.game.entity.metadata.VillagerData; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3i; @@ -101,11 +100,17 @@ public class VillagerEntity extends AbstractMerchantEntity { @Override public void moveRelative(GeyserSession session, double relX, double relY, double relZ, Vector3f rotation, boolean isOnGround) { + if (!metadata.getFlags().getFlag(EntityFlag.SLEEPING)) { + // No need to worry about extra processing to compensate for sleeping + super.moveRelative(session, relX, relY, relZ, rotation, isOnGround); + return; + } + int z = 0; int bedId = 0; float bedPositionSubtractorW = 0; float bedPositionSubtractorN = 0; - Vector3i bedPosition = metadata.getPos(EntityData.BED_POSITION); + Vector3i bedPosition = metadata.getPos(EntityData.BED_POSITION, null); if (session.getConnector().getConfig().isCacheChunks() && bedPosition != null) { bedId = session.getConnector().getWorldManager().getBlockAt(session, bedPosition); } @@ -117,39 +122,33 @@ public class VillagerEntity extends AbstractMerchantEntity { MoveEntityAbsolutePacket moveEntityPacket = new MoveEntityAbsolutePacket(); moveEntityPacket.setRuntimeEntityId(geyserId); //Sets Villager position and rotation when sleeping - if (!metadata.getFlags().getFlag(EntityFlag.SLEEPING)) { - moveEntityPacket.setPosition(position); - moveEntityPacket.setRotation(getBedrockRotation()); - } else { - //String Setup - Pattern r = Pattern.compile("facing=([a-z]+)"); - Matcher m = r.matcher(bedRotationZ); - if (m.find()) { - switch (m.group(0)) { - case "facing=south": - //bed is facing south - z = 180; - bedPositionSubtractorW = -.5f; - break; - case "facing=east": - //bed is facing east - z = 90; - bedPositionSubtractorW = -.5f; - break; - case "facing=west": - //bed is facing west - z = 270; - bedPositionSubtractorW = .5f; - break; - case "facing=north": - //rotation does not change because north is 0 - bedPositionSubtractorN = .5f; - break; - } + Pattern r = Pattern.compile("facing=([a-z]+)"); + Matcher m = r.matcher(bedRotationZ); + if (m.find()) { + switch (m.group(0)) { + case "facing=south": + //bed is facing south + z = 180; + bedPositionSubtractorW = -.5f; + break; + case "facing=east": + //bed is facing east + z = 90; + bedPositionSubtractorW = -.5f; + break; + case "facing=west": + //bed is facing west + z = 270; + bedPositionSubtractorW = .5f; + break; + case "facing=north": + //rotation does not change because north is 0 + bedPositionSubtractorN = .5f; + break; } - moveEntityPacket.setRotation(Vector3f.from(0, 0, z)); - moveEntityPacket.setPosition(Vector3f.from(position.getX() + bedPositionSubtractorW, position.getY(), position.getZ() + bedPositionSubtractorN)); } + moveEntityPacket.setRotation(Vector3f.from(0, 0, z)); + moveEntityPacket.setPosition(Vector3f.from(position.getX() + bedPositionSubtractorW, position.getY(), position.getZ() + bedPositionSubtractorN)); moveEntityPacket.setOnGround(isOnGround); moveEntityPacket.setTeleported(false); session.sendUpstreamPacket(moveEntityPacket); diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/ChunkCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/ChunkCache.java index bf1f4718e..d182a6f12 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/cache/ChunkCache.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/ChunkCache.java @@ -34,6 +34,7 @@ import org.geysermc.connector.network.translators.world.block.BlockTranslator; import org.geysermc.connector.utils.MathUtils; public class ChunkCache { + private static final int MINIMUM_WORLD_HEIGHT = 0; private final boolean cache; @@ -86,6 +87,11 @@ public class ChunkCache { return; } + if (y < MINIMUM_WORLD_HEIGHT || (y >> 4) > column.getChunks().length - 1) { + // Y likely goes above or below the height limit of this world + return; + } + Chunk chunk = column.getChunks()[y >> 4]; if (chunk != null) { chunk.set(x & 0xF, y & 0xF, z & 0xF, block); @@ -102,6 +108,11 @@ public class ChunkCache { return BlockTranslator.JAVA_AIR_ID; } + if (y < MINIMUM_WORLD_HEIGHT || (y >> 4) > column.getChunks().length - 1) { + // Y likely goes above or below the height limit of this world + return BlockTranslator.JAVA_AIR_ID; + } + Chunk chunk = column.getChunks()[y >> 4]; if (chunk != null) { return chunk.get(x & 0xF, y & 0xF, z & 0xF);