From ce000a496bfbdec6924606c0904b594d15cdcd82 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sun, 2 May 2021 21:47:11 -0400 Subject: [PATCH 1/9] Use tags sent from the server, where possible (#2188) Java Edition has a tags system that allows for some server-side control of certain properties. This PR allows for piglin trading items, wool, and flowers to be determined from the server. --- .../entity/living/animal/AnimalEntity.java | 4 +- .../entity/living/animal/BeeEntity.java | 14 +--- .../entity/living/animal/ChickenEntity.java | 4 +- .../entity/living/animal/FoxEntity.java | 3 +- .../entity/living/animal/HoglinEntity.java | 3 +- .../entity/living/animal/OcelotEntity.java | 3 +- .../entity/living/animal/PandaEntity.java | 3 +- .../entity/living/animal/PigEntity.java | 3 +- .../entity/living/animal/PolarBearEntity.java | 3 +- .../entity/living/animal/RabbitEntity.java | 3 +- .../entity/living/animal/StriderEntity.java | 3 +- .../entity/living/animal/TurtleEntity.java | 3 +- .../animal/horse/AbstractHorseEntity.java | 3 +- .../living/animal/horse/LlamaEntity.java | 3 +- .../living/animal/tameable/CatEntity.java | 3 +- .../living/animal/tameable/ParrotEntity.java | 3 +- .../living/animal/tameable/WolfEntity.java | 3 +- .../entity/living/monster/PiglinEntity.java | 2 +- .../network/session/GeyserSession.java | 2 + .../network/session/cache/TagCache.java | 74 ++++++++++++++++++ .../BedrockBlockPickRequestTranslator.java | 2 +- .../player/BedrockActionTranslator.java | 10 ++- .../collision/CollisionTranslator.java | 2 +- .../translators/item/ItemRegistry.java | 9 +-- .../java/JavaDeclareTagsTranslator.java | 40 ++++++++++ .../java/JavaJoinGameTranslator.java | 2 + .../player/JavaPlayerActionAckTranslator.java | 12 +-- .../world/JavaBlockBreakAnimTranslator.java | 2 +- .../FeedBabySoundInteractionHandler.java | 3 +- .../world/block/BlockTranslator.java | 70 ++++++++--------- .../connector/registry/type/BlockMapping.java | 75 +++++++++++++++++++ .../geysermc/connector/utils/BlockUtils.java | 22 ++++-- .../utils/InteractiveTagManager.java | 2 +- 33 files changed, 295 insertions(+), 98 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/network/session/cache/TagCache.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareTagsTranslator.java create mode 100644 connector/src/main/java/org/geysermc/connector/registry/type/BlockMapping.java 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 63cdc6ece..081ad859a 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 @@ -146,6 +146,7 @@ public class GeyserSession implements CommandSender { private ChunkCache chunkCache; private EntityCache entityCache; private EntityEffectCache effectCache; + private final TagCache tagCache; private WorldCache worldCache; private WindowCache windowCache; private final Int2ObjectMap teleportMap = new Int2ObjectOpenHashMap<>(); @@ -452,6 +453,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.windowCache = new WindowCache(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..350c029ff 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 @@ -46,6 +46,6 @@ 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 abd79437f..47918440b 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 @@ -63,6 +63,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..8aa06abc2 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/registry/type/BlockMapping.java @@ -0,0 +1,75 @@ +/* + * 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]; + } + + /** + * 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/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 { From 2aa131f9dceed6d5d72a37758374a945e8290e9b Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sun, 9 May 2021 01:11:42 -0400 Subject: [PATCH 2/9] Fix minecart rotation when not on rails (#2200) This isn't perfect, but it's better than before, for sure. --- .../java/org/geysermc/connector/entity/MinecartEntity.java | 6 ++++++ 1 file changed, 6 insertions(+) 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); + } } From dda0172dedde65131dbe3c61170ddb9163096f6b Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sun, 9 May 2021 01:19:06 -0400 Subject: [PATCH 3/9] Allow more blocks to have a place sound on standalone (#2183) Items such as wall torch blocks currently do not have a place sound on standalone, as their block identifier differs from their item identifier. This commit uses the pick item logic in order to fix place sounds for such blocks. --- .../java/world/JavaBlockChangeTranslator.java | 2 +- .../connector/registry/type/BlockMapping.java | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaBlockChangeTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaBlockChangeTranslator.java index e362e335f..de9563d9b 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaBlockChangeTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaBlockChangeTranslator.java @@ -68,7 +68,7 @@ public class JavaBlockChangeTranslator extends PacketTranslator Date: Sun, 9 May 2021 01:25:57 -0400 Subject: [PATCH 4/9] Fix visual glitch when swapping out armor that's already equipped (#2173) Bedrock Edition allows you to swap out armor by right-clicking an item in your inventory, even when armor in that slot is already equipped. This PR prevents Bedrock from performing this action if both slots are occupied (which Java Edition will not do). --- .../BedrockInventoryTransactionTranslator.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) 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 99d2de0b4..7dd670197 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 @@ -238,7 +238,16 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator Date: Sun, 9 May 2021 15:44:41 -0400 Subject: [PATCH 5/9] Item frame optimization and block picking support (#2203) Geyser now supports block picking for item frames. It checks to see if the item frame has an item in it - if so, it attempts the same block picking process with the item inside (NBT included). Otherwise, it attempts to pick for an item frame item. This commit also improves item frames by having the internal map store the entity and not the ID - in many situations, this prevents two maps from having to be searched. Additionally, item frames are no longer despawned if an item is placed on them - rather, it waits until the server tells us to despawn the entity. --- .../connector/entity/ItemFrameEntity.java | 31 +++---- .../network/session/GeyserSession.java | 10 +-- .../BedrockBlockPickRequestTranslator.java | 13 +++ .../BedrockEntityPickRequestTranslator.java | 2 +- ...BedrockInventoryTransactionTranslator.java | 30 ++++--- .../BedrockItemFrameDropItemTranslator.java | 11 ++- .../player/BedrockActionTranslator.java | 6 +- .../geysermc/connector/utils/ChunkUtils.java | 19 ++--- .../connector/utils/InventoryUtils.java | 80 ++++++++++++++++--- 9 files changed, 137 insertions(+), 65 deletions(-) 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/network/session/GeyserSession.java b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java index 081ad859a..28aff40b9 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; @@ -81,6 +78,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; @@ -189,10 +187,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. 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 350c029ff..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,6 +44,18 @@ public class BedrockBlockPickRequestTranslator extends PacketTranslator Date: Mon, 10 May 2021 00:48:01 -0400 Subject: [PATCH 6/9] Interact with an optional resource pack to add more features (#2176) See https://github.com/GeyserMC/Geyser/wiki/GeyserOptionalPack --- README.md | 3 +- .../connector/entity/AbstractArrowEntity.java | 3 + .../connector/entity/attribute/Attribute.java | 2 + .../entity/attribute/AttributeType.java | 10 +- .../entity/living/ArmorStandEntity.java | 78 +++++++++ .../entity/living/IronGolemEntity.java | 80 +++++++++ .../entity/living/animal/RabbitEntity.java | 5 +- .../entity/living/monster/ShulkerEntity.java | 3 + .../raid/SpellcasterIllagerEntity.java | 2 + .../connector/entity/player/PlayerEntity.java | 11 ++ .../connector/entity/type/EntityType.java | 8 +- .../entity/JavaEntityAnimationTranslator.java | 34 +++- .../world/JavaSpawnParticleTranslator.java | 162 ++++++++++++------ connector/src/main/resources/mappings | 2 +- 14 files changed, 330 insertions(+), 73 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/entity/living/IronGolemEntity.java diff --git a/README.md b/README.md index bc267f06f..a51c61f9f 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,8 @@ The following things cannot be fixed without changes to Bedrock. As of now, they - Custom heads in inventories - Clickable links in chat - Glowing effect -- Custom armor stand poses + +Do note that some things require the [GeyserOptionalPack](https://github.com/GeyserMC/Geyser/wiki/GeyserOptionalPack) in order to function, such as custom armor stand poses. ## Compiling 1. Clone the repo to your computer diff --git a/connector/src/main/java/org/geysermc/connector/entity/AbstractArrowEntity.java b/connector/src/main/java/org/geysermc/connector/entity/AbstractArrowEntity.java index 70dbdf959..25e8d37a1 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/AbstractArrowEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/AbstractArrowEntity.java @@ -36,6 +36,9 @@ public class AbstractArrowEntity extends Entity { public AbstractArrowEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { super(entityId, geyserId, entityType, position, motion, rotation); + // Set the correct texture if using the resource pack + metadata.getFlags().setFlag(EntityFlag.BRIBED, entityType == EntityType.SPECTRAL_ARROW); + setMotion(motion); } diff --git a/connector/src/main/java/org/geysermc/connector/entity/attribute/Attribute.java b/connector/src/main/java/org/geysermc/connector/entity/attribute/Attribute.java index 2a2d47ba9..9cb803898 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/attribute/Attribute.java +++ b/connector/src/main/java/org/geysermc/connector/entity/attribute/Attribute.java @@ -28,10 +28,12 @@ package org.geysermc.connector.entity.attribute; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.Setter; +import lombok.ToString; @Getter @Setter @AllArgsConstructor +@ToString public class Attribute { private AttributeType type; diff --git a/connector/src/main/java/org/geysermc/connector/entity/attribute/AttributeType.java b/connector/src/main/java/org/geysermc/connector/entity/attribute/AttributeType.java index ccd0bcb5b..6877bb7c6 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/attribute/AttributeType.java +++ b/connector/src/main/java/org/geysermc/connector/entity/attribute/AttributeType.java @@ -57,12 +57,12 @@ public enum AttributeType { HUNGER(null, "minecraft:player.hunger", 0f, 20f, 20f), SATURATION(null, "minecraft:player.saturation", 0f, 20f, 20f); - private String javaIdentifier; - private String bedrockIdentifier; + private final String javaIdentifier; + private final String bedrockIdentifier; - private float minimum; - private float maximum; - private float defaultValue; + private final float minimum; + private final float maximum; + private final float defaultValue; public Attribute getAttribute(float value) { return getAttribute(value, maximum); diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/ArmorStandEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/ArmorStandEntity.java index 3d1005510..13f8a4c1d 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/ArmorStandEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/ArmorStandEntity.java @@ -27,6 +27,7 @@ package org.geysermc.connector.entity.living; 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.Rotation; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; @@ -157,6 +158,72 @@ public class ArmorStandEntity extends LivingEntity { updateSecondEntityStatus(false); } + + // The following values don't do anything on normal Bedrock. + // But if given a resource pack, then we can use these values to control armor stand visual properties + metadata.getFlags().setFlag(EntityFlag.ANGRY, (xd & 0x04) != 0x04); // Has arms + metadata.getFlags().setFlag(EntityFlag.ADMIRING, (xd & 0x08) == 0x08); // Has no baseplate + } else { + EntityData dataLeech = null; + EntityFlag negativeXToggle = null; + EntityFlag negativeYToggle = null; + EntityFlag negativeZToggle = null; + switch (entityMetadata.getId()) { + case 15: // Head + dataLeech = EntityData.MARK_VARIANT; + negativeXToggle = EntityFlag.INTERESTED; + negativeYToggle = EntityFlag.CHARGED; + negativeZToggle = EntityFlag.POWERED; + break; + case 16: // Body + dataLeech = EntityData.VARIANT; + negativeXToggle = EntityFlag.IN_LOVE; + negativeYToggle = EntityFlag.CELEBRATING; + negativeZToggle = EntityFlag.CELEBRATING_SPECIAL; + break; + case 17: // Left arm + dataLeech = EntityData.TRADE_TIER; + negativeXToggle = EntityFlag.CHARGING; + negativeYToggle = EntityFlag.CRITICAL; + negativeZToggle = EntityFlag.DANCING; + break; + case 18: // Right arm + dataLeech = EntityData.MAX_TRADE_TIER; + negativeXToggle = EntityFlag.ELDER; + negativeYToggle = EntityFlag.EMOTING; + negativeZToggle = EntityFlag.IDLING; + break; + case 19: // Left leg + dataLeech = EntityData.SKIN_ID; + negativeXToggle = EntityFlag.IS_ILLAGER_CAPTAIN; + negativeYToggle = EntityFlag.IS_IN_UI; + negativeZToggle = EntityFlag.LINGERING; + break; + case 20: // Right leg + dataLeech = EntityData.HURT_DIRECTION; + negativeXToggle = EntityFlag.IS_PREGNANT; + negativeYToggle = EntityFlag.SHEARED; + negativeZToggle = EntityFlag.STALKING; + break; + } + if (dataLeech != null) { + // Indicate that rotation should be checked + metadata.getFlags().setFlag(EntityFlag.BRIBED, true); + + Rotation rotation = (Rotation) entityMetadata.getValue(); + int rotationX = getRotation(rotation.getPitch()); + int rotationY = getRotation(rotation.getYaw()); + int rotationZ = getRotation(rotation.getRoll()); + // The top bit acts like binary and determines if each rotation goes above 100 + // We don't do this for the negative values out of concerns of the number being too big + int topBit = (Math.abs(rotationX) >= 100 ? 4 : 0) + (Math.abs(rotationY) >= 100 ? 2 : 0) + (Math.abs(rotationZ) >= 100 ? 1 : 0); + int value = (topBit * 1000000) + ((Math.abs(rotationX) % 100) * 10000) + ((Math.abs(rotationY) % 100) * 100) + (Math.abs(rotationZ) % 100); + metadata.put(dataLeech, value); + // Set the entity flags if a value is negative + metadata.getFlags().setFlag(negativeXToggle, rotationX < 0); + metadata.getFlags().setFlag(negativeYToggle, rotationY < 0); + metadata.getFlags().setFlag(negativeZToggle, rotationZ < 0); + } } if (secondEntity != null) { secondEntity.updateBedrockMetadata(entityMetadata, session); @@ -302,6 +369,17 @@ public class ArmorStandEntity extends LivingEntity { } } + private int getRotation(float rotation) { + rotation = rotation % 360f; + if (rotation < -180f) { + rotation += 360f; + } else if (rotation >= 180f) { + // 181 -> -179 + rotation = -(180 - (rotation - 180)); + } + return (int) rotation; + } + /** * If this armor stand is not a marker, set its bounding box size and scale. */ diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/IronGolemEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/IronGolemEntity.java new file mode 100644 index 000000000..5fca355f5 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/entity/living/IronGolemEntity.java @@ -0,0 +1,80 @@ +/* + * 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.entity.living; + +import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.protocol.bedrock.data.AttributeData; +import com.nukkitx.protocol.bedrock.data.entity.EntityData; +import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; +import com.nukkitx.protocol.bedrock.packet.UpdateAttributesPacket; +import org.geysermc.connector.entity.attribute.AttributeType; +import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.utils.AttributeUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class IronGolemEntity extends GolemEntity { + + public IronGolemEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { + super(entityId, geyserId, entityType, position, motion, rotation); + // Indicate that we should show cracks through a resource pack + metadata.getFlags().setFlag(EntityFlag.BRIBED, true); + // Required, or else the overlay is black + metadata.put(EntityData.COLOR_2, (byte) 0); + } + + @Override + public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { + super.updateBedrockMetadata(entityMetadata, session); + if (entityMetadata.getId() == 8) { + // Required so the resource pack sees the entity health + attributes.put(AttributeType.HEALTH, AttributeType.HEALTH.getAttribute(metadata.getFloat(EntityData.HEALTH), 100f)); + updateBedrockAttributes(session); + } + } + + @Override + public void updateBedrockAttributes(GeyserSession session) { + if (!valid) return; + + List attributes = new ArrayList<>(); + for (Map.Entry entry : this.attributes.entrySet()) { + if (!entry.getValue().getType().isBedrockAttribute()) + continue; + + attributes.add(AttributeUtils.getBedrockAttribute(entry.getValue())); + } + + UpdateAttributesPacket updateAttributesPacket = new UpdateAttributesPacket(); + updateAttributesPacket.setRuntimeEntityId(geyserId); + updateAttributesPacket.setAttributes(attributes); + session.sendUpstreamPacket(updateAttributesPacket); + } +} 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 9137cd23a..9a4691cc0 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 @@ -53,9 +53,12 @@ public class RabbitEntity extends AnimalEntity { int variant = (int) entityMetadata.getValue(); // Change the killer bunny to display as white since it only exists on Java Edition - if (variant == 99) { + boolean isKillerBunny = variant == 99; + if (isKillerBunny) { variant = 1; } + // Allow the resource pack to adjust to the killer bunny + metadata.getFlags().setFlag(EntityFlag.BRIBED, isKillerBunny); metadata.put(EntityData.VARIANT, variant); } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/ShulkerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/ShulkerEntity.java index f31dde69c..b99f66ac8 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/ShulkerEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/ShulkerEntity.java @@ -31,6 +31,7 @@ import com.github.steveice10.mc.protocol.data.game.world.block.BlockFace; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3i; import com.nukkitx.protocol.bedrock.data.entity.EntityData; +import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.connector.entity.living.GolemEntity; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; @@ -39,6 +40,8 @@ public class ShulkerEntity extends GolemEntity { public ShulkerEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { super(entityId, geyserId, entityType, position, motion, rotation); + // Indicate that invisibility should be fixed through the resource pack + metadata.getFlags().setFlag(EntityFlag.BRIBED, true); } @Override diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/SpellcasterIllagerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/SpellcasterIllagerEntity.java index d43bd8fb7..6d4500a1c 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/SpellcasterIllagerEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/SpellcasterIllagerEntity.java @@ -39,6 +39,8 @@ public class SpellcasterIllagerEntity extends AbstractIllagerEntity { public SpellcasterIllagerEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { super(entityId, geyserId, entityType, position, motion, rotation); + // OptionalPack usage + metadata.getFlags().setFlag(EntityFlag.BRIBED, this.entityType == EntityType.ILLUSIONER); } @Override diff --git a/connector/src/main/java/org/geysermc/connector/entity/player/PlayerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/player/PlayerEntity.java index b8be69ab3..f8eeef307 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/player/PlayerEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/player/PlayerEntity.java @@ -82,6 +82,9 @@ public class PlayerEntity extends LivingEntity { profile = gameProfile; uuid = gameProfile.getId(); username = gameProfile.getName(); + + // For the OptionalPack, set all bits as invisible by default as this matches Java Edition behavior + metadata.put(EntityData.MARK_VARIANT, 0xff); } @Override @@ -280,6 +283,14 @@ public class PlayerEntity extends LivingEntity { session.sendUpstreamPacket(attributesPacket); } + if (entityMetadata.getId() == 16) { + // OptionalPack usage for toggling skin bits + // In Java Edition, a bit being set means that part should be enabled + // However, to ensure that the pack still works on other servers, we invert the bit so all values by default + // are true (0). + metadata.put(EntityData.MARK_VARIANT, ~((byte) entityMetadata.getValue()) & 0xff); + } + // Parrot occupying shoulder if (entityMetadata.getId() == 18 || entityMetadata.getId() == 19) { CompoundTag tag = (CompoundTag) entityMetadata.getValue(); diff --git a/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java b/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java index 43658f63b..cf169a209 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java +++ b/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java @@ -55,7 +55,7 @@ public enum EntityType { SQUID(SquidEntity.class, 17, 0.8f), RABBIT(RabbitEntity.class, 18, 0.5f, 0.4f), BAT(BatEntity.class, 19, 0.9f, 0.5f), - IRON_GOLEM(GolemEntity.class, 20, 2.7f, 1.4f), + IRON_GOLEM(IronGolemEntity.class, 20, 2.7f, 1.4f), SNOW_GOLEM(SnowGolemEntity.class, 21, 1.9f, 0.7f), OCELOT(OcelotEntity.class, 22, 0.35f, 0.3f), HORSE(HorseEntity.class, 23, 1.6f, 1.3965f), @@ -147,7 +147,7 @@ public enum EntityType { EVOKER(SpellcasterIllagerEntity.class, 104, 1.95f, 0.6f, 0.6f, 0f, "minecraft:evocation_illager"), VEX(VexEntity.class, 105, 0.8f, 0.4f), ICE_BOMB(Entity.class, 106, 0f), - BALLOON(Entity.class, 107, 0f), //TODO + BALLOON(Entity.class, 107, 0f), PUFFERFISH(PufferFishEntity.class, 108, 0.7f, 0.7f), SALMON(AbstractFishEntity.class, 109, 0.5f, 0.7f), DROWNED(ZombieEntity.class, 110, 1.95f, 0.6f), @@ -168,9 +168,9 @@ public enum EntityType { ITEM_FRAME(ItemFrameEntity.class, 0, 0, 0), /** - * Not an entity in Bedrock, so we replace it with a Pillager + * Not an entity in Bedrock, so we replace it with an evoker */ - ILLUSIONER(AbstractIllagerEntity.class, 114, 1.8f, 0.6f, 0.6f, 1.62f, "minecraft:pillager"), + ILLUSIONER(SpellcasterIllagerEntity.class, 104, 1.8f, 0.6f, 0.6f, 1.62f, "minecraft:evocation_illager"), /** * Not an entity in Bedrock, but used for the Ender Dragon's multiple hitboxes diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityAnimationTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityAnimationTranslator.java index 735a5ea47..255c4f466 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityAnimationTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityAnimationTranslator.java @@ -25,22 +25,27 @@ package org.geysermc.connector.network.translators.java.entity; +import com.github.steveice10.mc.protocol.packet.ingame.server.entity.ServerEntityAnimationPacket; +import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.protocol.bedrock.packet.AnimateEntityPacket; +import com.nukkitx.protocol.bedrock.packet.AnimatePacket; +import com.nukkitx.protocol.bedrock.packet.SpawnParticleEffectPacket; import org.geysermc.connector.entity.Entity; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; - -import com.github.steveice10.mc.protocol.packet.ingame.server.entity.ServerEntityAnimationPacket; -import com.nukkitx.protocol.bedrock.packet.AnimatePacket; +import org.geysermc.connector.utils.DimensionUtils; @Translator(packet = ServerEntityAnimationPacket.class) public class JavaEntityAnimationTranslator extends PacketTranslator { @Override public void translate(ServerEntityAnimationPacket packet, GeyserSession session) { - Entity entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId()); + Entity entity; if (packet.getEntityId() == session.getPlayerEntity().getEntityId()) { entity = session.getPlayerEntity(); + } else { + entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId()); } if (entity == null) return; @@ -51,11 +56,30 @@ public class JavaEntityAnimationTranslator extends PacketTranslator { + private final Random random = new Random(); @Override public void translate(ServerSpawnParticlePacket packet, GeyserSession session) { - LevelEventPacket particle = new LevelEventPacket(); - switch (packet.getParticle().getType()) { - case BLOCK: - particle.setType(LevelEventType.PARTICLE_DESTROY_BLOCK_NO_SOUND); - particle.setPosition(Vector3f.from(packet.getX(), packet.getY(), packet.getZ())); - particle.setData(session.getBlockTranslator().getBedrockBlockId(((BlockParticleData) packet.getParticle().getData()).getBlockState())); - session.sendUpstreamPacket(particle); - break; - case FALLING_DUST: - //In fact, FallingDustParticle should have data like DustParticle, - //but in MCProtocol, its data is BlockState(1). - particle.setType(LevelEventType.PARTICLE_FALLING_DUST); - particle.setData(session.getBlockTranslator().getBedrockBlockId(((FallingDustParticleData)packet.getParticle().getData()).getBlockState())); - particle.setPosition(Vector3f.from(packet.getX(), packet.getY(), packet.getZ())); - session.sendUpstreamPacket(particle); - break; - case ITEM: - ItemStack javaItem = ((ItemParticleData)packet.getParticle().getData()).getItemStack(); - ItemData bedrockItem = ItemTranslator.translateToBedrock(session, javaItem); - int id = bedrockItem.getId(); - int damage = bedrockItem.getDamage(); - particle.setType(LevelEventType.PARTICLE_ITEM_BREAK); - particle.setData(id << 16 | damage); - particle.setPosition(Vector3f.from(packet.getX(), packet.getY(), packet.getZ())); - session.sendUpstreamPacket(particle); - break; - case DUST: - DustParticleData data = (DustParticleData)packet.getParticle().getData(); - int r = (int) (data.getRed()*255); - int g = (int) (data.getGreen()*255); - int b = (int) (data.getBlue()*255); - particle.setType(LevelEventType.PARTICLE_FALLING_DUST); - particle.setData(((0xff) << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff)); - particle.setPosition(Vector3f.from(packet.getX(), packet.getY(), packet.getZ())); - session.sendUpstreamPacket(particle); - break; - default: - LevelEventType typeParticle = EffectRegistry.getParticleLevelEventType(packet.getParticle().getType()); - if (typeParticle != null) { - particle.setType(typeParticle); - particle.setPosition(Vector3f.from(packet.getX(), packet.getY(), packet.getZ())); - session.sendUpstreamPacket(particle); - } else { - String stringParticle = EffectRegistry.getParticleString(packet.getParticle().getType()); - if (stringParticle != null) { - SpawnParticleEffectPacket stringPacket = new SpawnParticleEffectPacket(); - stringPacket.setIdentifier(stringParticle); - stringPacket.setDimensionId(DimensionUtils.javaToBedrock(session.getDimension())); - stringPacket.setPosition(Vector3f.from(packet.getX(), packet.getY(), packet.getZ())); - session.sendUpstreamPacket(stringPacket); - } + Function particleCreateFunction = createParticle(session, packet.getParticle()); + if (particleCreateFunction != null) { + if (packet.getAmount() == 0) { + // 0 means don't apply the offset + Vector3f position = Vector3f.from(packet.getX(), packet.getY(), packet.getZ()); + session.sendUpstreamPacket(particleCreateFunction.apply(position)); + } else { + for (int i = 0; i < packet.getAmount(); i++) { + double offsetX = this.random.nextGaussian() * (double) packet.getOffsetX(); + double offsetY = this.random.nextGaussian() * (double) packet.getOffsetY(); + double offsetZ = this.random.nextGaussian() * (double) packet.getOffsetZ(); + Vector3f position = Vector3f.from(packet.getX() + offsetX, packet.getY() + offsetY, packet.getZ() + offsetZ); + + session.sendUpstreamPacket(particleCreateFunction.apply(position)); } - break; + } + } else { + // Null is only returned when no particle of this type is found + session.getConnector().getLogger().debug("Unhandled particle packet: " + packet); } } + /** + * @param session the Bedrock client session. + * @param particle the Java particle to translate to a Bedrock equivalent. + * @return a function to create a packet with a specified particle, in the event we need to spawn multiple particles + * with different offsets. + */ + private Function createParticle(GeyserSession session, Particle particle) { + switch (particle.getType()) { + case BLOCK: { + int blockState = session.getBlockTranslator().getBedrockBlockId(((BlockParticleData) particle.getData()).getBlockState()); + return (position) -> { + LevelEventPacket packet = new LevelEventPacket(); + packet.setType(LevelEventType.PARTICLE_CRACK_BLOCK); + packet.setPosition(position); + packet.setData(blockState); + return packet; + }; + } + case FALLING_DUST: { + int blockState = session.getBlockTranslator().getBedrockBlockId(((FallingDustParticleData) particle.getData()).getBlockState()); + return (position) -> { + LevelEventPacket packet = new LevelEventPacket(); + // In fact, FallingDustParticle should have data like DustParticle, + // but in MCProtocol, its data is BlockState(1). + packet.setType(LevelEventType.PARTICLE_FALLING_DUST); + packet.setData(blockState); + packet.setPosition(position); + return packet; + }; + } + case ITEM: { + ItemStack javaItem = ((ItemParticleData) particle.getData()).getItemStack(); + ItemData bedrockItem = ItemTranslator.translateToBedrock(session, javaItem); + int data = bedrockItem.getId() << 16 | bedrockItem.getDamage(); + return (position) -> { + LevelEventPacket packet = new LevelEventPacket(); + packet.setType(LevelEventType.PARTICLE_ITEM_BREAK); + packet.setData(data); + packet.setPosition(position); + return packet; + }; + } + case DUST: { + DustParticleData data = (DustParticleData) particle.getData(); + int r = (int) (data.getRed() * 255); + int g = (int) (data.getGreen() * 255); + int b = (int) (data.getBlue() * 255); + int rgbData = ((0xff) << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff); + return (position) -> { + LevelEventPacket packet = new LevelEventPacket(); + packet.setType(LevelEventType.PARTICLE_FALLING_DUST); + packet.setData(rgbData); + packet.setPosition(position); + return packet; + }; + } + default: + LevelEventType typeParticle = EffectRegistry.getParticleLevelEventType(particle.getType()); + if (typeParticle != null) { + return (position) -> { + LevelEventPacket packet = new LevelEventPacket(); + packet.setType(typeParticle); + packet.setPosition(position); + return packet; + }; + } else { + String stringParticle = EffectRegistry.getParticleString(particle.getType()); + if (stringParticle != null) { + int dimensionId = DimensionUtils.javaToBedrock(session.getDimension()); + return (position) -> { + SpawnParticleEffectPacket stringPacket = new SpawnParticleEffectPacket(); + stringPacket.setIdentifier(stringParticle); + stringPacket.setDimensionId(dimensionId); + stringPacket.setPosition(position); + return stringPacket; + }; + } else { + return null; + } + } + } + } } \ No newline at end of file diff --git a/connector/src/main/resources/mappings b/connector/src/main/resources/mappings index c846b8200..53e13b7a0 160000 --- a/connector/src/main/resources/mappings +++ b/connector/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit c846b8200eb8ebb37207666f7eddb83f2b636c37 +Subproject commit 53e13b7a0d2ea14df71ed0c9582d29a9b4fb4453 From dbf366b138c581c4bbb139b3bdb353274fbfa62d Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Mon, 10 May 2021 21:14:30 -0400 Subject: [PATCH 7/9] ACTUALLY fix swapping armor in hotbar when armor is already present (#2206) Tested with iOS, desktop and console --- .../bedrock/BedrockInventoryTransactionTranslator.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) 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 2358fa278..6378c0ba9 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 @@ -39,10 +39,7 @@ import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3i; import com.nukkitx.protocol.bedrock.data.LevelEventType; import com.nukkitx.protocol.bedrock.data.entity.EntityFlags; -import com.nukkitx.protocol.bedrock.data.inventory.ContainerId; -import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; -import com.nukkitx.protocol.bedrock.data.inventory.InventoryActionData; -import com.nukkitx.protocol.bedrock.data.inventory.InventorySource; +import com.nukkitx.protocol.bedrock.data.inventory.*; import com.nukkitx.protocol.bedrock.packet.*; import org.geysermc.connector.entity.CommandBlockMinecartEntity; import org.geysermc.connector.entity.Entity; @@ -241,9 +238,10 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator 0) { InventoryActionData actionData = packet.getActions().get(0); - if (actionData.getSlot() == 6 && actionData.getToItem().getId() != 0) { + LegacySetItemSlotData slotData = packet.getLegacySlots().get(0); + if (slotData.getContainerId() == 6 && actionData.getToItem().getId() != 0) { // The player is trying to swap out an armor piece that already has an item in it // Java Edition does not allow this; let's revert it session.getInventoryTranslator().updateInventory(session, session.getPlayerInventory()); From 3e3b8faf16ad09fa8aeb436cf1de53b1c90f82cd Mon Sep 17 00:00:00 2001 From: Konicai <71294714+Konicai@users.noreply.github.com> Date: Fri, 14 May 2021 22:48:34 -0400 Subject: [PATCH 8/9] Add client setting for cooldown type, refactor show-coordinates (#2193) --- .../geysermc/connector/GeyserConnector.java | 2 +- .../network/session/GeyserSession.java | 4 +- .../session/cache/PreferencesCache.java | 71 +++++++++++++++++++ .../network/session/cache/WorldCache.java | 21 ------ .../connector/utils/CooldownUtils.java | 31 +++++--- .../connector/utils/SettingsUtils.java | 37 +++++++--- connector/src/main/resources/languages | 2 +- 7 files changed, 125 insertions(+), 43 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/network/session/cache/PreferencesCache.java diff --git a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java index 03bf7538c..d2caac216 100644 --- a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java +++ b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java @@ -188,7 +188,7 @@ public class GeyserConnector { defaultAuthType = AuthType.getByName(config.getRemote().getAuthType()); - CooldownUtils.setShowCooldown(config.getShowCooldown()); + CooldownUtils.setDefaultShowCooldown(config.getShowCooldown()); DimensionUtils.changeBedrockNetherId(config.isAboveBedrockNetherBuilding()); // Apply End dimension ID workaround to Nether SkullBlockEntityTranslator.ALLOW_CUSTOM_SKULLS = config.isAllowCustomSkulls(); 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 28aff40b9..3748a3ba2 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 @@ -144,6 +144,7 @@ public class GeyserSession implements CommandSender { private ChunkCache chunkCache; private EntityCache entityCache; private EntityEffectCache effectCache; + private final PreferencesCache preferencesCache; private final TagCache tagCache; private WorldCache worldCache; private WindowCache windowCache; @@ -451,6 +452,7 @@ public class GeyserSession implements CommandSender { this.chunkCache = new ChunkCache(this); this.entityCache = new EntityCache(this); this.effectCache = new EntityEffectCache(); + this.preferencesCache = new PreferencesCache(this); this.tagCache = new TagCache(); this.worldCache = new WorldCache(this); this.windowCache = new WindowCache(this); @@ -1224,7 +1226,7 @@ public class GeyserSession implements CommandSender { public void setReducedDebugInfo(boolean value) { reducedDebugInfo = value; // Set the showCoordinates data. This is done because updateShowCoordinates() uses this gamerule as a variable. - getWorldCache().updateShowCoordinates(); + preferencesCache.updateShowCoordinates(); } /** diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/PreferencesCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/PreferencesCache.java new file mode 100644 index 000000000..d477066c2 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/PreferencesCache.java @@ -0,0 +1,71 @@ +/* + * 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 lombok.Getter; +import lombok.Setter; +import org.geysermc.connector.configuration.GeyserConfiguration; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.utils.CooldownUtils; + +@Getter +public class PreferencesCache { + private final GeyserSession session; + + /** + * True if the client prefers being shown their coordinates, regardless if they're being shown or not. + * This will be true everytime the client joins the server because neither the client nor server store the preference permanently. + */ + @Setter + private boolean prefersShowCoordinates = true; + /** + * If the client's preference will be ignored, this will return false. + */ + private boolean allowShowCoordinates; + + /** + * Which CooldownType the client prefers. Initially set to {@link CooldownUtils#getDefaultShowCooldown()}. + */ + @Setter + private CooldownUtils.CooldownType cooldownPreference = CooldownUtils.getDefaultShowCooldown(); + + public PreferencesCache(GeyserSession session) { + this.session = session; + } + + /** + * Tell the client to hide or show the coordinates. + * + * If {@link #prefersShowCoordinates} is true, coordinates will be shown, unless either of the following conditions apply:
+ *
+ * {@link GeyserSession#reducedDebugInfo} is enabled + * {@link GeyserConfiguration#isShowCoordinates()} is disabled + */ + public void updateShowCoordinates() { + allowShowCoordinates = !session.isReducedDebugInfo() && session.getConnector().getConfig().isShowCoordinates(); + session.sendGameRule("showcoordinates", allowShowCoordinates && prefersShowCoordinates); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/WorldCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/WorldCache.java index 84678c211..4a2939621 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/cache/WorldCache.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/WorldCache.java @@ -28,7 +28,6 @@ package org.geysermc.connector.network.session.cache; import com.github.steveice10.mc.protocol.data.game.setting.Difficulty; import lombok.Getter; import lombok.Setter; -import org.geysermc.connector.configuration.GeyserConfiguration; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.scoreboard.Objective; import org.geysermc.connector.scoreboard.Scoreboard; @@ -40,13 +39,6 @@ public class WorldCache { @Setter private Difficulty difficulty = Difficulty.EASY; - /** - * True if the client prefers being shown their coordinates, regardless if they're being shown or not. - * This will be true everytime the client joins the server because neither the client nor server store the preference permanently. - */ - @Setter - private boolean prefersShowCoordinates = true; - private Scoreboard scoreboard; private final ScoreboardUpdater scoreboardUpdater; @@ -71,17 +63,4 @@ public class WorldCache { int pps = scoreboardUpdater.getPacketsPerSecond(); return Math.max(pps, pendingPps); } - - /** - * Tell the client to hide or show the coordinates. - * - * If {@link #prefersShowCoordinates} is true, coordinates will be shown, unless either of the following conditions apply:
- *
- * {@link GeyserSession#reducedDebugInfo} is enabled - * {@link GeyserConfiguration#isShowCoordinates()} is disabled - */ - public void updateShowCoordinates() { - boolean allowShowCoordinates = !session.isReducedDebugInfo() && session.getConnector().getConfig().isShowCoordinates(); - session.sendGameRule("showcoordinates", allowShowCoordinates && prefersShowCoordinates); - } } \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/utils/CooldownUtils.java b/connector/src/main/java/org/geysermc/connector/utils/CooldownUtils.java index 0b5c2bdd3..615143589 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/CooldownUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/CooldownUtils.java @@ -28,6 +28,7 @@ package org.geysermc.connector.utils; import com.nukkitx.protocol.bedrock.packet.SetTitlePacket; import lombok.Getter; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.session.cache.PreferencesCache; import java.util.concurrent.TimeUnit; @@ -36,18 +37,25 @@ import java.util.concurrent.TimeUnit; * Much of the work here is from the wonderful folks from ViaRewind: https://github.com/ViaVersion/ViaRewind */ public class CooldownUtils { - private static CooldownType SHOW_COOLDOWN; + private static CooldownType DEFAULT_SHOW_COOLDOWN; - public static void setShowCooldown(String showCooldown) { - SHOW_COOLDOWN = CooldownType.getByName(showCooldown); + public static void setDefaultShowCooldown(String showCooldown) { + DEFAULT_SHOW_COOLDOWN = CooldownType.getByName(showCooldown); + } + + public static CooldownType getDefaultShowCooldown() { + return DEFAULT_SHOW_COOLDOWN; } /** - * Starts sending the fake cooldown to the Bedrock client. + * Starts sending the fake cooldown to the Bedrock client. If the cooldown is not disabled, the sent type is {@link PreferencesCache#getCooldownPreference()} * @param session GeyserSession */ public static void sendCooldown(GeyserSession session) { - if (SHOW_COOLDOWN == CooldownType.DISABLED) return; + if (DEFAULT_SHOW_COOLDOWN == CooldownType.DISABLED) return; + CooldownType sessionPreference = session.getPreferencesCache().getCooldownPreference(); + if (sessionPreference == CooldownType.DISABLED) return; + if (session.getAttackSpeed() == 0.0 || session.getAttackSpeed() > 20) return; // 0.0 usually happens on login and causes issues with visuals; anything above 20 means a plugin like OldCombatMechanics is being used // Needs to be sent or no subtitle packet is recognized by the client SetTitlePacket titlePacket = new SetTitlePacket(); @@ -56,19 +64,20 @@ public class CooldownUtils { session.sendUpstreamPacket(titlePacket); session.setLastHitTime(System.currentTimeMillis()); long lastHitTime = session.getLastHitTime(); // Used later to prevent multiple scheduled cooldown threads - computeCooldown(session, lastHitTime); + computeCooldown(session, sessionPreference, lastHitTime); } /** * Keeps updating the cooldown until the bar is complete. * @param session GeyserSession + * @param sessionPreference The type of cooldown the client prefers * @param lastHitTime The time of the last hit. Used to gauge how long the cooldown is taking. */ - private static void computeCooldown(GeyserSession session, long lastHitTime) { + private static void computeCooldown(GeyserSession session, CooldownType sessionPreference, long lastHitTime) { if (session.isClosed()) return; // Don't run scheduled tasks if the client left if (lastHitTime != session.getLastHitTime()) return; // Means another cooldown has started so there's no need to continue this one SetTitlePacket titlePacket = new SetTitlePacket(); - if (SHOW_COOLDOWN == CooldownType.ACTIONBAR) { + if (sessionPreference == CooldownType.ACTIONBAR) { titlePacket.setType(SetTitlePacket.Type.ACTIONBAR); } else { titlePacket.setType(SetTitlePacket.Type.SUBTITLE); @@ -79,10 +88,10 @@ public class CooldownUtils { titlePacket.setStayTime(2); session.sendUpstreamPacket(titlePacket); if (hasCooldown(session)) { - session.getConnector().getGeneralThreadPool().schedule(() -> computeCooldown(session, lastHitTime), 50, TimeUnit.MILLISECONDS); // Updated per tick. 1000 divided by 20 ticks equals 50 + session.getConnector().getGeneralThreadPool().schedule(() -> computeCooldown(session, sessionPreference, lastHitTime), 50, TimeUnit.MILLISECONDS); // Updated per tick. 1000 divided by 20 ticks equals 50 } else { SetTitlePacket removeTitlePacket = new SetTitlePacket(); - if (SHOW_COOLDOWN == CooldownType.ACTIONBAR) { + if (sessionPreference == CooldownType.ACTIONBAR) { removeTitlePacket.setType(SetTitlePacket.Type.ACTIONBAR); } else { removeTitlePacket.setType(SetTitlePacket.Type.SUBTITLE); @@ -133,7 +142,7 @@ public class CooldownUtils { public static final CooldownType[] VALUES = values(); /** - * Convert the CooldownType string (from config) to the enum, TITLE on fail + * Convert the CooldownType string (from config) to the enum, DISABLED on fail * * @param name CooldownType string * diff --git a/connector/src/main/java/org/geysermc/connector/utils/SettingsUtils.java b/connector/src/main/java/org/geysermc/connector/utils/SettingsUtils.java index 1d06c8a0f..d0cfce862 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/SettingsUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/SettingsUtils.java @@ -57,11 +57,24 @@ public class SettingsUtils { CustomFormBuilder builder = new CustomFormBuilder(LanguageUtils.getPlayerLocaleString("geyser.settings.title.main", language)); builder.setIcon(new FormImage(FormImage.FormImageType.PATH, "textures/ui/settings_glyph_color_2x.png")); - // Client can only see its coordinates if reducedDebugInfo is disabled and coordinates are enabled in geyser config. - if (!session.isReducedDebugInfo() && session.getConnector().getConfig().isShowCoordinates()) { + // Only show the client title if any of the client settings are available + if (session.getPreferencesCache().isAllowShowCoordinates() || CooldownUtils.getDefaultShowCooldown() != CooldownUtils.CooldownType.DISABLED) { builder.addComponent(new LabelComponent(LanguageUtils.getPlayerLocaleString("geyser.settings.title.client", language))); - builder.addComponent(new ToggleComponent(LanguageUtils.getPlayerLocaleString("geyser.settings.option.coordinates", language), session.getWorldCache().isPrefersShowCoordinates())); + // Client can only see its coordinates if reducedDebugInfo is disabled and coordinates are enabled in geyser config. + if (session.getPreferencesCache().isAllowShowCoordinates()) { + builder.addComponent(new ToggleComponent(LanguageUtils.getPlayerLocaleString("geyser.settings.option.coordinates", language), session.getPreferencesCache().isPrefersShowCoordinates())); + } + + if (CooldownUtils.getDefaultShowCooldown() != CooldownUtils.CooldownType.DISABLED) { + DropdownComponent cooldownDropdown = new DropdownComponent(); + cooldownDropdown.setText(LocaleUtils.getLocaleString("options.attackIndicator", language)); + cooldownDropdown.setOptions(new ArrayList<>()); + cooldownDropdown.addOption(LocaleUtils.getLocaleString("options.attack.crosshair", language), session.getPreferencesCache().getCooldownPreference() == CooldownUtils.CooldownType.TITLE); + cooldownDropdown.addOption(LocaleUtils.getLocaleString("options.attack.hotbar", language), session.getPreferencesCache().getCooldownPreference() == CooldownUtils.CooldownType.ACTIONBAR); + cooldownDropdown.addOption(LocaleUtils.getLocaleString("options.off", language), session.getPreferencesCache().getCooldownPreference() == CooldownUtils.CooldownType.DISABLED); + builder.addComponent(cooldownDropdown); + } } @@ -121,13 +134,21 @@ public class SettingsUtils { } int offset = 0; - // Client can only see its coordinates if reducedDebugInfo is disabled and coordinates are enabled in geyser config. - if (!session.isReducedDebugInfo() && session.getConnector().getConfig().isShowCoordinates()) { + if (session.getPreferencesCache().isAllowShowCoordinates() || CooldownUtils.getDefaultShowCooldown() != CooldownUtils.CooldownType.DISABLED) { offset++; // Client settings title - session.getWorldCache().setPrefersShowCoordinates(settingsResponse.getToggleResponses().get(offset)); - session.getWorldCache().updateShowCoordinates(); - offset++; + // Client can only see its coordinates if reducedDebugInfo is disabled and coordinates are enabled in geyser config. + if (session.getPreferencesCache().isAllowShowCoordinates()) { + session.getPreferencesCache().setPrefersShowCoordinates(settingsResponse.getToggleResponses().get(offset)); + session.getPreferencesCache().updateShowCoordinates(); + offset++; + } + + if (CooldownUtils.getDefaultShowCooldown() != CooldownUtils.CooldownType.DISABLED) { + CooldownUtils.CooldownType cooldownType = CooldownUtils.CooldownType.VALUES[settingsResponse.getDropdownResponses().get(offset).getElementID()]; + session.getPreferencesCache().setCooldownPreference(cooldownType); + offset++; + } } if (session.getOpPermissionLevel() >= 2 || session.hasPermission("geyser.settings.server")) { diff --git a/connector/src/main/resources/languages b/connector/src/main/resources/languages index 96e7ed66c..9b08df518 160000 --- a/connector/src/main/resources/languages +++ b/connector/src/main/resources/languages @@ -1 +1 @@ -Subproject commit 96e7ed66ccdafea0cc991b8004566d448e8f6e6a +Subproject commit 9b08df51898fd71ee24e7accdfbe56f164b5c539 From dfc24f1ecb1f626002b1eacc79916471b7047ada Mon Sep 17 00:00:00 2001 From: Konicai <71294714+Konicai@users.noreply.github.com> Date: Fri, 14 May 2021 23:12:37 -0400 Subject: [PATCH 9/9] Fix Javadoc in CooldownUtils (#2213) --- .../main/java/org/geysermc/connector/utils/CooldownUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/connector/src/main/java/org/geysermc/connector/utils/CooldownUtils.java b/connector/src/main/java/org/geysermc/connector/utils/CooldownUtils.java index 615143589..43675ebfd 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/CooldownUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/CooldownUtils.java @@ -48,7 +48,7 @@ public class CooldownUtils { } /** - * Starts sending the fake cooldown to the Bedrock client. If the cooldown is not disabled, the sent type is {@link PreferencesCache#getCooldownPreference()} + * Starts sending the fake cooldown to the Bedrock client. If the cooldown is not disabled, the sent type is the cooldownPreference in {@link PreferencesCache} * @param session GeyserSession */ public static void sendCooldown(GeyserSession session) {