diff --git a/connector/pom.xml b/connector/pom.xml index 07fb8be75..ea5d3c516 100644 --- a/connector/pom.xml +++ b/connector/pom.xml @@ -43,7 +43,7 @@ com.github.CloudburstMC.Protocol bedrock-v431 - f8ecf54 + 9947665 compile diff --git a/connector/src/main/java/org/geysermc/connector/entity/ItemFrameEntity.java b/connector/src/main/java/org/geysermc/connector/entity/ItemFrameEntity.java index e10ad0afd..79711b0cb 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/ItemFrameEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/ItemFrameEntity.java @@ -35,6 +35,7 @@ import com.nukkitx.nbt.NbtMapBuilder; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket; import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket; +import lombok.Getter; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.item.ItemEntry; @@ -69,6 +70,11 @@ public class ItemFrameEntity extends Entity { * Cached item frame's Bedrock compound tag. */ private NbtMap cachedTag; + /** + * The item currently in the item frame. Used for block picking. + */ + @Getter + private ItemStack heldItem = null; public ItemFrameEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation, HangingDirection direction) { super(entityId, geyserId, entityType, position, motion, rotation); @@ -87,7 +93,8 @@ public class ItemFrameEntity extends Entity { bedrockRuntimeId = session.getBlockTranslator().getItemFrame(blockBuilder.build()); bedrockPosition = Vector3i.from(position.getFloorX(), position.getFloorY(), position.getFloorZ()); - session.getItemFrameCache().put(bedrockPosition, entityId); + session.getItemFrameCache().put(bedrockPosition, this); + // Delay is required, or else loading in frames on chunk load is sketchy at best session.getConnector().getGeneralThreadPool().schedule(() -> { updateBlock(session); @@ -99,13 +106,14 @@ public class ItemFrameEntity extends Entity { @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { if (entityMetadata.getId() == 7 && entityMetadata.getValue() != null) { - ItemData itemData = ItemTranslator.translateToBedrock(session, (ItemStack) entityMetadata.getValue()); + this.heldItem = (ItemStack) entityMetadata.getValue(); + ItemData itemData = ItemTranslator.translateToBedrock(session, heldItem); ItemEntry itemEntry = ItemRegistry.getItem((ItemStack) entityMetadata.getValue()); NbtMapBuilder builder = NbtMap.builder(); builder.putByte("Count", (byte) itemData.getCount()); if (itemData.getTag() != null) { - builder.put("tag", itemData.getTag().toBuilder().build()); + builder.put("tag", itemData.getTag()); } builder.putShort("Damage", (short) itemData.getDamage()); builder.putString("Name", itemEntry.getBedrockIdentifier()); @@ -146,7 +154,9 @@ public class ItemFrameEntity extends Entity { updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK); updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS); session.sendUpstreamPacket(updateBlockPacket); - session.getItemFrameCache().remove(position, entityId); + + session.getItemFrameCache().remove(bedrockPosition, this); + valid = false; return true; } @@ -192,16 +202,7 @@ public class ItemFrameEntity extends Entity { * @param session GeyserSession. * @return Java entity ID or -1 if not found. */ - public static long getItemFrameEntityId(GeyserSession session, Vector3i position) { - return session.getItemFrameCache().getOrDefault(position, -1); - } - - /** - * Force-remove from the position-to-ID map so it doesn't cause conflicts. - * @param session GeyserSession. - * @param position position of the removed item frame. - */ - public static void removePosition(GeyserSession session, Vector3i position) { - session.getItemFrameCache().remove(position); + public static ItemFrameEntity getItemFrameEntity(GeyserSession session, Vector3i position) { + return session.getItemFrameCache().get(position); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/MinecartEntity.java b/connector/src/main/java/org/geysermc/connector/entity/MinecartEntity.java index 49b12a3e1..b66b049eb 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/MinecartEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/MinecartEntity.java @@ -79,4 +79,10 @@ public class MinecartEntity extends Entity { public void moveAbsolute(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) { super.moveAbsolute(session, position.add(0d, this.entityType.getOffset(), 0d), rotation, isOnGround, teleported); } + + @Override + public Vector3f getBedrockRotation() { + // Note: minecart rotation on rails does not care about the actual rotation value + return Vector3f.from(0, rotation.getX(), 0); + } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/AnimalEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/AnimalEntity.java index 075690662..efded1f6f 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/AnimalEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/AnimalEntity.java @@ -28,6 +28,8 @@ package org.geysermc.connector.entity.living.animal; import com.nukkitx.math.vector.Vector3f; import org.geysermc.connector.entity.living.AgeableEntity; import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.item.ItemEntry; public class AnimalEntity extends AgeableEntity { @@ -40,7 +42,7 @@ public class AnimalEntity extends AgeableEntity { * wheat. * @return true if this is a valid item to breed with for this animal. */ - public boolean canEat(String javaIdentifierStripped) { + public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemEntry itemEntry) { // This is what it defaults to. OK. return javaIdentifierStripped.equals("wheat"); } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/BeeEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/BeeEntity.java index d076b8a6f..b2d5faaad 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/BeeEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/BeeEntity.java @@ -26,7 +26,6 @@ package org.geysermc.connector.entity.living.animal; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; -import com.google.common.collect.ImmutableSet; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityEventType; @@ -34,16 +33,9 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.packet.EntityEventPacket; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; - -import java.util.Set; +import org.geysermc.connector.network.translators.item.ItemEntry; public class BeeEntity extends AnimalEntity { - /** - * 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"); public BeeEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { super(entityId, geyserId, entityType, position, motion, rotation); @@ -74,7 +66,7 @@ public class BeeEntity extends AnimalEntity { } @Override - public boolean canEat(String javaIdentifierStripped) { - return FLOWERS.contains(javaIdentifierStripped); + public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemEntry itemEntry) { + return session.getTagCache().isFlower(itemEntry); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/ChickenEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/ChickenEntity.java index 66e8f9ac8..b5f0395d4 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/ChickenEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/ChickenEntity.java @@ -27,6 +27,8 @@ package org.geysermc.connector.entity.living.animal; import com.nukkitx.math.vector.Vector3f; import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.item.ItemEntry; public class ChickenEntity extends AnimalEntity { @@ -35,7 +37,7 @@ public class ChickenEntity extends AnimalEntity { } @Override - public boolean canEat(String javaIdentifierStripped) { + public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemEntry itemEntry) { return javaIdentifierStripped.contains("seeds"); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/FoxEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/FoxEntity.java index bfa376272..ff71e87f0 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/FoxEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/FoxEntity.java @@ -31,6 +31,7 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.item.ItemEntry; public class FoxEntity extends AnimalEntity { @@ -54,7 +55,7 @@ public class FoxEntity extends AnimalEntity { } @Override - public boolean canEat(String javaIdentifierStripped) { + public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemEntry itemEntry) { return javaIdentifierStripped.equals("sweet_berries"); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/HoglinEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/HoglinEntity.java index b915c07de..9fbb17725 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/HoglinEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/HoglinEntity.java @@ -30,6 +30,7 @@ import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.item.ItemEntry; import org.geysermc.connector.utils.DimensionUtils; public class HoglinEntity extends AnimalEntity { @@ -49,7 +50,7 @@ public class HoglinEntity extends AnimalEntity { } @Override - public boolean canEat(String javaIdentifierStripped) { + public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemEntry itemEntry) { return javaIdentifierStripped.equals("crimson_fungus"); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/OcelotEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/OcelotEntity.java index 7c7d74770..1cf541c8c 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/OcelotEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/OcelotEntity.java @@ -30,6 +30,7 @@ import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.item.ItemEntry; public class OcelotEntity extends AnimalEntity { @@ -46,7 +47,7 @@ public class OcelotEntity extends AnimalEntity { } @Override - public boolean canEat(String javaIdentifierStripped) { + public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemEntry itemEntry) { return javaIdentifierStripped.equals("cod") || javaIdentifierStripped.equals("salmon"); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/PandaEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/PandaEntity.java index 9431d66a0..2f5ced080 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/PandaEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/PandaEntity.java @@ -33,6 +33,7 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.packet.EntityEventPacket; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.item.ItemEntry; import org.geysermc.connector.network.translators.item.ItemRegistry; public class PandaEntity extends AnimalEntity { @@ -80,7 +81,7 @@ public class PandaEntity extends AnimalEntity { } @Override - public boolean canEat(String javaIdentifierStripped) { + public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemEntry itemEntry) { return javaIdentifierStripped.equals("bamboo"); } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/PigEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/PigEntity.java index 551e1c345..bbb1aed20 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/PigEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/PigEntity.java @@ -30,6 +30,7 @@ import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.item.ItemEntry; public class PigEntity extends AnimalEntity { @@ -47,7 +48,7 @@ public class PigEntity extends AnimalEntity { } @Override - public boolean canEat(String javaIdentifierStripped) { + public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemEntry itemEntry) { return javaIdentifierStripped.equals("carrot") || javaIdentifierStripped.equals("potato") || javaIdentifierStripped.equals("beetroot"); } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/PolarBearEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/PolarBearEntity.java index 7b5d42f35..f33635aeb 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/PolarBearEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/PolarBearEntity.java @@ -30,6 +30,7 @@ import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.item.ItemEntry; public class PolarBearEntity extends AnimalEntity { @@ -46,7 +47,7 @@ public class PolarBearEntity extends AnimalEntity { } @Override - public boolean canEat(String javaIdentifierStripped) { + public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemEntry itemEntry) { return false; } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/RabbitEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/RabbitEntity.java index a789b48e4..9137cd23a 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/RabbitEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/RabbitEntity.java @@ -31,6 +31,7 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.item.ItemEntry; public class RabbitEntity extends AnimalEntity { @@ -61,7 +62,7 @@ public class RabbitEntity extends AnimalEntity { } @Override - public boolean canEat(String javaIdentifierStripped) { + public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemEntry itemEntry) { return javaIdentifierStripped.equals("dandelion") || javaIdentifierStripped.equals("carrot") || javaIdentifierStripped.equals("golden_carrot"); } } \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/StriderEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/StriderEntity.java index 2a99a5a03..a90a044bc 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/StriderEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/StriderEntity.java @@ -31,6 +31,7 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.connector.entity.Entity; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.item.ItemEntry; public class StriderEntity extends AnimalEntity { @@ -87,7 +88,7 @@ public class StriderEntity extends AnimalEntity { } @Override - public boolean canEat(String javaIdentifierStripped) { + public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemEntry itemEntry) { return javaIdentifierStripped.equals("warped_fungus"); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/TurtleEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/TurtleEntity.java index 89df815d7..536f40755 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/TurtleEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/TurtleEntity.java @@ -30,6 +30,7 @@ import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.item.ItemEntry; public class TurtleEntity extends AnimalEntity { @@ -48,7 +49,7 @@ public class TurtleEntity extends AnimalEntity { } @Override - public boolean canEat(String javaIdentifierStripped) { + public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemEntry itemEntry) { return javaIdentifierStripped.equals("seagrass"); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/AbstractHorseEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/AbstractHorseEntity.java index d2f89e101..324c8229f 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/AbstractHorseEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/AbstractHorseEntity.java @@ -37,6 +37,7 @@ import org.geysermc.connector.entity.attribute.AttributeType; import org.geysermc.connector.entity.living.animal.AnimalEntity; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.item.ItemEntry; import org.geysermc.connector.network.translators.item.ItemRegistry; import java.util.Set; @@ -112,7 +113,7 @@ public class AbstractHorseEntity extends AnimalEntity { } @Override - public boolean canEat(String javaIdentifierStripped) { + public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemEntry itemEntry) { return DONKEY_AND_HORSE_FOODS.contains(javaIdentifierStripped); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/LlamaEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/LlamaEntity.java index 0b21c771e..8ab1df1a8 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/LlamaEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/LlamaEntity.java @@ -32,6 +32,7 @@ import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import com.nukkitx.protocol.bedrock.packet.MobArmorEquipmentPacket; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.item.ItemEntry; import org.geysermc.connector.network.translators.item.ItemRegistry; public class LlamaEntity extends ChestedHorseEntity { @@ -77,7 +78,7 @@ public class LlamaEntity extends ChestedHorseEntity { } @Override - public boolean canEat(String javaIdentifierStripped) { + public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemEntry itemEntry) { return javaIdentifierStripped.equals("wheat") || javaIdentifierStripped.equals("hay_block"); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/CatEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/CatEntity.java index 7866d25cf..2d56d0c18 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/CatEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/CatEntity.java @@ -31,6 +31,7 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.item.ItemEntry; public class CatEntity extends TameableEntity { @@ -85,7 +86,7 @@ public class CatEntity extends TameableEntity { } @Override - public boolean canEat(String javaIdentifierStripped) { + public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemEntry itemEntry) { return javaIdentifierStripped.equals("cod") || javaIdentifierStripped.equals("salmon"); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/ParrotEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/ParrotEntity.java index 50ec9ed04..45327c785 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/ParrotEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/ParrotEntity.java @@ -30,6 +30,7 @@ import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.item.ItemEntry; public class ParrotEntity extends TameableEntity { @@ -47,7 +48,7 @@ public class ParrotEntity extends TameableEntity { } @Override - public boolean canEat(String javaIdentifierStripped) { + public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemEntry itemEntry) { return javaIdentifierStripped.contains("seeds") || javaIdentifierStripped.equals("cookie"); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/WolfEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/WolfEntity.java index 91350ef54..bb7916937 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/WolfEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/WolfEntity.java @@ -32,6 +32,7 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.item.ItemEntry; import java.util.Set; @@ -87,7 +88,7 @@ public class WolfEntity extends TameableEntity { } @Override - public boolean canEat(String javaIdentifierStripped) { + public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemEntry itemEntry) { // Cannot be a baby to eat these foods return WOLF_FOODS.contains(javaIdentifierStripped) && !metadata.getFlags().getFlag(EntityFlag.BABY); } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/PiglinEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/PiglinEntity.java index e6e509b11..44fa2c49d 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/PiglinEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/PiglinEntity.java @@ -61,7 +61,7 @@ public class PiglinEntity extends BasePiglinEntity { @Override public void updateOffHand(GeyserSession session) { // Check if the Piglin is holding Gold and set the ADMIRING flag accordingly so its pose updates - boolean changed = metadata.getFlags().setFlag(EntityFlag.ADMIRING, offHand.getId() == ItemRegistry.GOLD.getBedrockId()); + boolean changed = metadata.getFlags().setFlag(EntityFlag.ADMIRING, session.getTagCache().shouldPiglinAdmire(ItemRegistry.getItem(this.offHand))); if (changed) { super.updateBedrockMetadata(session); } 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 2ee0bb770..b922903b6 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 @@ -66,10 +66,7 @@ import it.unimi.dsi.fastutil.ints.IntList; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; -import it.unimi.dsi.fastutil.objects.Object2LongMap; -import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; -import it.unimi.dsi.fastutil.objects.ObjectIterator; -import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import it.unimi.dsi.fastutil.objects.*; import lombok.AccessLevel; import lombok.Getter; import lombok.NonNull; @@ -79,6 +76,7 @@ import org.geysermc.connector.command.CommandSender; import org.geysermc.connector.common.AuthType; import org.geysermc.connector.configuration.EmoteOffhandWorkaroundOption; import org.geysermc.connector.entity.Entity; +import org.geysermc.connector.entity.ItemFrameEntity; import org.geysermc.connector.entity.Tickable; import org.geysermc.connector.entity.attribute.Attribute; import org.geysermc.connector.entity.attribute.AttributeType; @@ -143,6 +141,7 @@ public class GeyserSession implements CommandSender { private ChunkCache chunkCache; private EntityCache entityCache; private EntityEffectCache effectCache; + private final TagCache tagCache; private WorldCache worldCache; private FormCache formCache; private final Int2ObjectMap teleportMap = new Int2ObjectOpenHashMap<>(); @@ -185,10 +184,10 @@ public class GeyserSession implements CommandSender { private final Long2ObjectMap storedMaps = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>()); /** - * A map of Vector3i positions to Java entity IDs. + * A map of Vector3i positions to Java entities. * Used for translating Bedrock block actions to Java entity actions. */ - private final Object2LongMap itemFrameCache = new Object2LongOpenHashMap<>(); + private final Map itemFrameCache = new Object2ObjectOpenHashMap<>(); /** * Stores a list of all lectern locations and their block entity tags. @@ -446,6 +445,7 @@ public class GeyserSession implements CommandSender { this.chunkCache = new ChunkCache(this); this.entityCache = new EntityCache(this); this.effectCache = new EntityEffectCache(); + this.tagCache = new TagCache(); this.worldCache = new WorldCache(this); this.formCache = new FormCache(this); diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/TagCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/TagCache.java new file mode 100644 index 000000000..c46e37b74 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/TagCache.java @@ -0,0 +1,74 @@ +/* + * 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.session.cache; + +import com.github.steveice10.mc.protocol.packet.ingame.server.ServerDeclareTagsPacket; +import it.unimi.dsi.fastutil.ints.IntList; +import it.unimi.dsi.fastutil.ints.IntLists; +import org.geysermc.connector.network.translators.item.ItemEntry; +import org.geysermc.connector.registry.type.BlockMapping; + +import java.util.Map; + +/** + * Manages information sent from the {@link ServerDeclareTagsPacket}. If that packet is not sent, all lists here + * will remain empty, matching Java Edition behavior. + */ +public class TagCache { + /* Blocks */ + private IntList wool = IntLists.emptyList(); + /* Items */ + private IntList flowers = IntLists.emptyList(); + private IntList piglinLoved = IntLists.emptyList(); + + public void loadPacket(ServerDeclareTagsPacket packet) { + Map blockTags = packet.getBlockTags(); + this.wool = IntList.of(blockTags.get("minecraft:wool")); + + Map itemTags = packet.getItemTags(); + this.flowers = IntList.of(itemTags.get("minecraft:flowers")); + this.piglinLoved = IntList.of(itemTags.get("minecraft:piglin_loved")); + } + + public void clear() { + this.wool = IntLists.emptyList(); + + this.flowers = IntLists.emptyList(); + this.piglinLoved = IntLists.emptyList(); + } + + public boolean isFlower(ItemEntry itemEntry) { + return flowers.contains(itemEntry.getJavaId()); + } + + public boolean shouldPiglinAdmire(ItemEntry itemEntry) { + return piglinLoved.contains(itemEntry.getJavaId()); + } + + public boolean isWool(BlockMapping blockMapping) { + return wool.contains(blockMapping.getJavaBlockId()); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockBlockPickRequestTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockBlockPickRequestTranslator.java index 9becfb36f..ba74c7769 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockBlockPickRequestTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockBlockPickRequestTranslator.java @@ -27,6 +27,7 @@ package org.geysermc.connector.network.translators.bedrock; import com.nukkitx.math.vector.Vector3i; import com.nukkitx.protocol.bedrock.packet.BlockPickRequestPacket; +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; @@ -43,9 +44,21 @@ public class BedrockBlockPickRequestTranslator extends PacketTranslator type : collisionTypes) { CollisionRemapper annotation = annotationMap.get(type); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java index 10c14074f..0e1a28d54 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java @@ -108,10 +108,6 @@ public class ItemRegistry { * Egg item entry, used in JavaEntityStatusTranslator.java */ public static ItemEntry EGG; - /** - * Gold item entry, used in PiglinEntity.java - */ - public static ItemEntry GOLD; /** * Shield item entry, used in Entity.java and LivingEntity.java */ @@ -294,7 +290,7 @@ public class ItemRegistry { // However, in order for some visuals and crafting to work, we need to send the first matching block state // as indexed by Bedrock's block palette // There are exceptions! But, ideally, the block ID override should take care of those. - String javaBlockIdentifier = BlockTranslator.getJavaIdBlockMap().inverse().get(blockRuntimeIdNode.intValue()).split("\\[")[0]; + String javaBlockIdentifier = BlockTranslator.getBlockMapping(blockRuntimeIdNode.intValue()).getCleanJavaIdentifier(); NbtMapBuilder requiredBlockStatesBuilder = NbtMap.builder(); String correctBedrockIdentifier = blockTranslator.getAllBedrockBlockStates().get(aValidBedrockBlockId).getString("name"); boolean firstPass = true; @@ -437,9 +433,6 @@ public class ItemRegistry { case "minecraft:egg": EGG = itemEntry; break; - case "minecraft:gold_ingot": - GOLD = itemEntry; - break; case "minecraft:shield": SHIELD = itemEntry; break; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareTagsTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareTagsTranslator.java new file mode 100644 index 000000000..368693bf2 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareTagsTranslator.java @@ -0,0 +1,40 @@ +/* + * 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.translators.java; + +import com.github.steveice10.mc.protocol.packet.ingame.server.ServerDeclareTagsPacket; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.PacketTranslator; +import org.geysermc.connector.network.translators.Translator; + +@Translator(packet = ServerDeclareTagsPacket.class) +public class JavaDeclareTagsTranslator extends PacketTranslator { + + @Override + public void translate(ServerDeclareTagsPacket packet, GeyserSession session) { + session.getTagCache().loadPacket(packet); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaJoinGameTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaJoinGameTranslator.java index d983289e8..f38232911 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaJoinGameTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaJoinGameTranslator.java @@ -64,6 +64,8 @@ public class JavaJoinGameTranslator extends PacketTranslator itemFrames = new Object2IntOpenHashMap<>(); private final Map flowerPotBlocks = new HashMap<>(); - public static final Int2DoubleMap JAVA_RUNTIME_ID_TO_HARDNESS = new Int2DoubleOpenHashMap(); - public static final Int2BooleanMap JAVA_RUNTIME_ID_TO_CAN_HARVEST_WITH_HAND = new Int2BooleanOpenHashMap(); - public static final Int2ObjectMap JAVA_RUNTIME_ID_TO_TOOL_TYPE = new Int2ObjectOpenHashMap<>(); - - // The index of the collision data in collision.json - public static final Int2IntMap JAVA_RUNTIME_ID_TO_COLLISION_INDEX = new Int2IntOpenHashMap(); - - private static final Int2ObjectMap JAVA_RUNTIME_ID_TO_PICK_ITEM = new Int2ObjectOpenHashMap<>(); + private static final Int2ObjectMap JAVA_RUNTIME_ID_TO_BLOCK_MAPPING = new Int2ObjectOpenHashMap<>(); /** * Java numeric ID to java unique identifier, used for block names in the statistics screen @@ -96,11 +90,7 @@ public abstract class BlockTranslator { private final EmptyChunkProvider emptyChunkProvider; - /** - * A list of all Java runtime wool IDs, for use with block breaking math and shears - */ - public static final IntSet JAVA_RUNTIME_WOOL_IDS = new IntOpenHashSet(); - public static final int JAVA_RUNTIME_COBWEB_ID; + public static final int JAVA_COBWEB_BLOCK_ID; public static final int JAVA_RUNTIME_FURNACE_ID; public static final int JAVA_RUNTIME_FURNACE_LIT_ID; @@ -127,7 +117,7 @@ public abstract class BlockTranslator { } int javaRuntimeId = -1; - int cobwebRuntimeId = -1; + int cobwebBlockId = -1; int furnaceRuntimeId = -1; int furnaceLitRuntimeId = -1; int spawnerRuntimeId = -1; @@ -139,31 +129,35 @@ public abstract class BlockTranslator { Map.Entry entry = blocksIterator.next(); String javaId = entry.getKey(); + BlockMapping.BlockMappingBuilder builder = BlockMapping.builder(); // TODO fix this, (no block should have a null hardness) JsonNode hardnessNode = entry.getValue().get("block_hardness"); if (hardnessNode != null) { - JAVA_RUNTIME_ID_TO_HARDNESS.put(javaRuntimeId, hardnessNode.doubleValue()); + builder.hardness(hardnessNode.doubleValue()); } - try { - JAVA_RUNTIME_ID_TO_CAN_HARVEST_WITH_HAND.put(javaRuntimeId, entry.getValue().get("can_break_with_hand").booleanValue()); - } catch (Exception e) { - JAVA_RUNTIME_ID_TO_CAN_HARVEST_WITH_HAND.put(javaRuntimeId, false); + JsonNode canBreakWithHandNode = entry.getValue().get("can_break_with_hand"); + if (canBreakWithHandNode != null) { + builder.canBreakWithHand(canBreakWithHandNode.booleanValue()); + } else { + builder.canBreakWithHand(false); } JsonNode toolTypeNode = entry.getValue().get("tool_type"); if (toolTypeNode != null) { - JAVA_RUNTIME_ID_TO_TOOL_TYPE.put(javaRuntimeId, toolTypeNode.textValue()); + builder.toolType(toolTypeNode.textValue()); + } else { + builder.toolType(""); } JsonNode collisionIndexNode = entry.getValue().get("collision_index"); if (hardnessNode != null) { - JAVA_RUNTIME_ID_TO_COLLISION_INDEX.put(javaRuntimeId, collisionIndexNode.intValue()); + builder.collisionIndex(collisionIndexNode.intValue()); } JsonNode pickItemNode = entry.getValue().get("pick_item"); if (pickItemNode != null) { - JAVA_RUNTIME_ID_TO_PICK_ITEM.put(javaRuntimeId, pickItemNode.textValue()); + builder.pickItem(pickItemNode.textValue()); } JAVA_ID_BLOCK_MAP.put(javaId, javaRuntimeId); @@ -183,11 +177,14 @@ public abstract class BlockTranslator { JAVA_TO_BEDROCK_IDENTIFIERS.put(cleanJavaIdentifier, bedrockIdentifier); } - if (javaId.contains("wool")) { - JAVA_RUNTIME_WOOL_IDS.add(javaRuntimeId); + builder.javaBlockId(uniqueJavaId); - } else if (javaId.contains("cobweb")) { - cobwebRuntimeId = javaRuntimeId; + builder.javaIdentifier(javaId); + + JAVA_RUNTIME_ID_TO_BLOCK_MAPPING.put(javaRuntimeId, builder.build()); + + if (javaId.contains("cobweb")) { + cobwebBlockId = uniqueJavaId; } else if (javaId.startsWith("minecraft:furnace[facing=north")) { if (javaId.contains("lit=true")) { @@ -204,10 +201,10 @@ public abstract class BlockTranslator { } } - if (cobwebRuntimeId == -1) { + if (cobwebBlockId == -1) { throw new AssertionError("Unable to find cobwebs in palette"); } - JAVA_RUNTIME_COBWEB_ID = cobwebRuntimeId; + JAVA_COBWEB_BLOCK_ID = cobwebBlockId; if (furnaceRuntimeId == -1) { throw new AssertionError("Unable to find furnace in palette"); @@ -229,6 +226,8 @@ public abstract class BlockTranslator { } JAVA_WATER_ID = waterRuntimeId; + BlockMapping.AIR = JAVA_RUNTIME_ID_TO_BLOCK_MAPPING.get(JAVA_AIR_ID); + BlockTranslator1_16_100.init(); BlockTranslator1_16_210.init(); BLOCKS_JSON = null; // We no longer require this so let it garbage collect away @@ -457,18 +456,11 @@ public abstract class BlockTranslator { } /** - * Get the item a Java client would receive when pressing - * the Pick Block key on a specific Java block state. - * - * @param javaId The Java runtime id of the block - * @return The Java identifier of the item + * @param javaRuntimeId the Java runtime ID of the block to search for. + * @return the corresponding block mapping for this runtime ID. */ - public static String getPickItem(int javaId) { - String itemIdentifier = JAVA_RUNTIME_ID_TO_PICK_ITEM.get(javaId); - if (itemIdentifier == null) { - return JAVA_ID_BLOCK_MAP.inverse().get(javaId).split("\\[")[0]; - } - return itemIdentifier; + public static BlockMapping getBlockMapping(int javaRuntimeId) { + return JAVA_RUNTIME_ID_TO_BLOCK_MAPPING.getOrDefault(javaRuntimeId, BlockMapping.AIR); } /** diff --git a/connector/src/main/java/org/geysermc/connector/registry/type/BlockMapping.java b/connector/src/main/java/org/geysermc/connector/registry/type/BlockMapping.java new file mode 100644 index 000000000..f3eaddd91 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/registry/type/BlockMapping.java @@ -0,0 +1,87 @@ +/* + * 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.registry.type; + +import lombok.Builder; +import lombok.Value; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +@Builder +@Value +public class BlockMapping { + public static BlockMapping AIR; + + String javaIdentifier; + /** + * The block ID shared between all different block states of this block. + * NOT the runtime ID! + */ + int javaBlockId; + + double hardness; + boolean canBreakWithHand; + @Nonnull String toolType; + /** + * The index of this collision in collision.json + */ + int collisionIndex; + @Nullable String pickItem; + + /** + * @return the identifier without the additional block states + */ + public String getCleanJavaIdentifier() { + return javaIdentifier.split("\\[")[0]; + } + + /** + * @return the corresponding Java identifier for this item + */ + public String getItemIdentifier() { + if (pickItem != null && !pickItem.equals("minecraft:air")) { + // Spawners can have air as their pick item which we are not interested in. + return pickItem; + } + + return getCleanJavaIdentifier(); + } + + /** + * Get the item a Java client would receive when pressing + * the Pick Block key on a specific Java block state. + * + * @return The Java identifier of the item + */ + public String getPickItem() { + if (pickItem != null) { + return pickItem; + } + + return getCleanJavaIdentifier(); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/utils/BlockUtils.java b/connector/src/main/java/org/geysermc/connector/utils/BlockUtils.java index 997e4aee4..01c85ab43 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/BlockUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/BlockUtils.java @@ -33,6 +33,7 @@ import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.item.ItemEntry; import org.geysermc.connector.network.translators.item.ToolItemEntry; import org.geysermc.connector.network.translators.world.block.BlockTranslator; +import org.geysermc.connector.registry.type.BlockMapping; public class BlockUtils { /** @@ -108,11 +109,11 @@ public class BlockUtils { return 1.0 / speed; } - public static double getBreakTime(double blockHardness, int blockId, ItemEntry item, CompoundTag nbtData, GeyserSession session) { - boolean isWoolBlock = BlockTranslator.JAVA_RUNTIME_WOOL_IDS.contains(blockId); - boolean isCobweb = blockId == BlockTranslator.JAVA_RUNTIME_COBWEB_ID; - String blockToolType = BlockTranslator.JAVA_RUNTIME_ID_TO_TOOL_TYPE.getOrDefault(blockId, ""); - boolean canHarvestWithHand = BlockTranslator.JAVA_RUNTIME_ID_TO_CAN_HARVEST_WITH_HAND.get(blockId); + public static double getBreakTime(GeyserSession session, BlockMapping blockMapping, ItemEntry item, CompoundTag nbtData, boolean isSessionPlayer) { + boolean isWoolBlock = session.getTagCache().isWool(blockMapping); + boolean isCobweb = blockMapping.getJavaBlockId() == BlockTranslator.JAVA_COBWEB_BLOCK_ID; + String blockToolType = blockMapping.getToolType(); + boolean canHarvestWithHand = blockMapping.isCanBreakWithHand(); String toolType = ""; String toolTier = ""; boolean correctTool = false; @@ -126,8 +127,11 @@ public class BlockUtils { int hasteLevel = 0; int miningFatigueLevel = 0; - if (session == null) { - return calculateBreakTime(blockHardness, toolTier, canHarvestWithHand, correctTool, toolType, isWoolBlock, isCobweb, toolEfficiencyLevel, hasteLevel, miningFatigueLevel, false, false, false); + if (!isSessionPlayer) { + // Another entity is currently mining; we have all the information we know + return calculateBreakTime(blockMapping.getHardness(), toolTier, canHarvestWithHand, correctTool, toolType, isWoolBlock, + isCobweb, toolEfficiencyLevel, hasteLevel, miningFatigueLevel, false, + false, false); } hasteLevel = session.getEffectCache().getEffectLevel(Effect.FASTER_DIG); @@ -140,7 +144,9 @@ public class BlockUtils { boolean outOfWaterButNotOnGround = (!isInWater) && (!session.getPlayerEntity().isOnGround()); boolean insideWaterNotOnGround = isInWater && !session.getPlayerEntity().isOnGround(); - return calculateBreakTime(blockHardness, toolTier, canHarvestWithHand, correctTool, toolType, isWoolBlock, isCobweb, toolEfficiencyLevel, hasteLevel, miningFatigueLevel, insideOfWaterWithoutAquaAffinity, outOfWaterButNotOnGround, insideWaterNotOnGround); + return calculateBreakTime(blockMapping.getHardness(), toolTier, canHarvestWithHand, correctTool, toolType, isWoolBlock, + isCobweb, toolEfficiencyLevel, hasteLevel, miningFatigueLevel, insideOfWaterWithoutAquaAffinity, + outOfWaterButNotOnGround, insideWaterNotOnGround); } /** 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 b6e387237..d7de053ce 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java @@ -47,7 +47,6 @@ import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import lombok.Data; import lombok.experimental.UtilityClass; import org.geysermc.connector.GeyserConnector; -import org.geysermc.connector.entity.Entity; import org.geysermc.connector.entity.ItemFrameEntity; import org.geysermc.connector.entity.player.SkullPlayerEntity; import org.geysermc.connector.network.session.GeyserSession; @@ -329,21 +328,13 @@ public class ChunkUtils { */ public static void updateBlock(GeyserSession session, int blockState, Vector3i position) { // Checks for item frames so they aren't tripped up and removed - long frameEntityId = ItemFrameEntity.getItemFrameEntityId(session, position); - if (frameEntityId != -1) { - // TODO: Very occasionally the item frame doesn't sync up when destroyed - Entity entity = session.getEntityCache().getEntityByJavaId(frameEntityId); - if (blockState == JAVA_AIR_ID && entity != null) { // Item frame is still present and no block overrides that; refresh it - ((ItemFrameEntity) entity).updateBlock(session); + ItemFrameEntity itemFrameEntity = ItemFrameEntity.getItemFrameEntity(session, position); + if (itemFrameEntity != null) { + if (blockState == JAVA_AIR_ID) { // Item frame is still present and no block overrides that; refresh it + itemFrameEntity.updateBlock(session); return; } - - // Otherwise the item frame is gone - if (entity != null) { - session.getEntityCache().removeEntity(entity, false); - } else { - ItemFrameEntity.removePosition(session, position); - } + // Otherwise, let's still store our reference to the item frame, but let the new block take precedence for now } SkullPlayerEntity skull = session.getSkullCache().get(position); diff --git a/connector/src/main/java/org/geysermc/connector/utils/InteractiveTagManager.java b/connector/src/main/java/org/geysermc/connector/utils/InteractiveTagManager.java index ff7ad11cf..1d6cf60d9 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/InteractiveTagManager.java +++ b/connector/src/main/java/org/geysermc/connector/utils/InteractiveTagManager.java @@ -82,7 +82,7 @@ public class InteractiveTagManager { // 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 if (interactEntity instanceof AnimalEntity && ((AnimalEntity) interactEntity).canEat(javaIdentifierStripped)) { + } else if (interactEntity instanceof AnimalEntity && ((AnimalEntity) interactEntity).canEat(session, javaIdentifierStripped, itemEntry)) { // This animal can be fed interactiveTag = InteractiveTag.FEED; } else { diff --git a/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java b/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java index d4759acaa..d9480d466 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java @@ -170,6 +170,60 @@ public class InventoryUtils { .tag(root.build()).build(); } + /** + * See {@link #findOrCreateItem(GeyserSession, String)}. This is for finding a specified {@link ItemStack}. + * + * @param session the Bedrock client's session + * @param itemStack the item to try to find a match for. NBT will also be accounted for. + */ + public static void findOrCreateItem(GeyserSession session, ItemStack itemStack) { + PlayerInventory inventory = session.getPlayerInventory(); + + if (itemStack == null || itemStack.getId() == 0) { + return; + } + + // Check hotbar for item + for (int i = 36; i < 45; i++) { + GeyserItemStack geyserItem = inventory.getItem(i); + if (geyserItem.isEmpty()) { + continue; + } + // If this is the item we're looking for + if (geyserItem.getJavaId() == itemStack.getId() && Objects.equals(geyserItem.getNbt(), itemStack.getNbt())) { + setHotbarItem(session, i); + // Don't check inventory if item was in hotbar + return; + } + } + + // Check inventory for item + for (int i = 9; i < 36; i++) { + GeyserItemStack geyserItem = inventory.getItem(i); + if (geyserItem.isEmpty()) { + continue; + } + // If this is the item we're looking for + if (geyserItem.getJavaId() == itemStack.getId() && Objects.equals(geyserItem.getNbt(), itemStack.getNbt())) { + ClientMoveItemToHotbarPacket packetToSend = new ClientMoveItemToHotbarPacket(i); // https://wiki.vg/Protocol#Pick_Item + session.sendDownstreamPacket(packetToSend); + return; + } + } + + // If we still have not found the item, and we're in creative, ask for the item from the server. + if (session.getGameMode() == GameMode.CREATIVE) { + int slot = findEmptyHotbarSlot(inventory); + + ClientCreativeInventoryActionPacket actionPacket = new ClientCreativeInventoryActionPacket(slot, + itemStack); + if ((slot - 36) != inventory.getHeldItemSlot()) { + setHotbarItem(session, slot); + } + session.sendDownstreamPacket(actionPacket); + } + } + /** * Attempt to find the specified item name in the session's inventory. * If it is found and in the hotbar, set the user's held item to that slot. @@ -223,15 +277,7 @@ public class InventoryUtils { // If we still have not found the item, and we're in creative, ask for the item from the server. if (session.getGameMode() == GameMode.CREATIVE) { - int slot = inventory.getHeldItemSlot() + 36; - if (!inventory.getItemInHand().isEmpty()) { // Otherwise we should just use the current slot - for (int i = 36; i < 45; i++) { - if (inventory.getItem(i).isEmpty()) { - slot = i; - break; - } - } - } + int slot = findEmptyHotbarSlot(inventory); ItemEntry entry = ItemRegistry.getItemEntry(itemName); if (entry != null) { @@ -247,6 +293,22 @@ public class InventoryUtils { } } + /** + * @return the first empty slot found in this inventory, or else the player's currently held slot. + */ + private static int findEmptyHotbarSlot(PlayerInventory inventory) { + int slot = inventory.getHeldItemSlot() + 36; + if (!inventory.getItemInHand().isEmpty()) { // Otherwise we should just use the current slot + for (int i = 36; i < 45; i++) { + if (inventory.getItem(i).isEmpty()) { + slot = i; + break; + } + } + } + return slot; + } + /** * Changes the held item slot to the specified slot * @param session GeyserSession diff --git a/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java b/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java index f68ee0e4c..ce9f23272 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java @@ -59,6 +59,8 @@ import java.util.UUID; public class LoginEncryptionUtils { private static final ObjectMapper JSON_MAPPER = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + private static boolean HAS_SENT_ENCRYPTION_MESSAGE = false; + private static boolean validateChainData(JsonNode data) throws Exception { ECPublicKey lastKey = null; boolean validChain = false; @@ -136,7 +138,18 @@ public class LoginEncryptionUtils { session.setClientData(data); if (EncryptionUtils.canUseEncryption()) { - LoginEncryptionUtils.startEncryptionHandshake(session, identityPublicKey); + try { + LoginEncryptionUtils.startEncryptionHandshake(session, identityPublicKey); + } catch (Throwable e) { + // An error can be thrown on older Java 8 versions about an invalid key + if (connector.getConfig().isDebugMode()) { + e.printStackTrace(); + } + + sendEncryptionFailedMessage(connector); + } + } else { + sendEncryptionFailedMessage(connector); } } catch (Exception ex) { session.disconnect("disconnectionScreen.internalError.cantConnect"); @@ -158,6 +171,14 @@ public class LoginEncryptionUtils { session.sendUpstreamPacketImmediately(packet); } + private static void sendEncryptionFailedMessage(GeyserConnector connector) { + if (!HAS_SENT_ENCRYPTION_MESSAGE) { + connector.getLogger().warning(LanguageUtils.getLocaleStringLog("geyser.network.encryption.line_1")); + connector.getLogger().warning(LanguageUtils.getLocaleStringLog("geyser.network.encryption.line_2", "https://geysermc.org/supported_java")); + HAS_SENT_ENCRYPTION_MESSAGE = true; + } + } + public static void buildAndShowLoginWindow(GeyserSession session) { // Set DoDaylightCycle to false so the time doesn't accelerate while we're here session.setDaylightCycle(false); diff --git a/connector/src/main/resources/languages b/connector/src/main/resources/languages index 3d3b60de7..96e7ed66c 160000 --- a/connector/src/main/resources/languages +++ b/connector/src/main/resources/languages @@ -1 +1 @@ -Subproject commit 3d3b60de724f3f552f351c5f400269fde7598b67 +Subproject commit 96e7ed66ccdafea0cc991b8004566d448e8f6e6a