diff --git a/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java b/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java index 2c5fe7a86..991b95571 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java +++ b/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java @@ -25,6 +25,7 @@ package org.geysermc.geyser.entity; +import org.geysermc.geyser.entity.type.living.monster.raid.RavagerEntity; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.MetadataType; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.FloatEntityMetadata; @@ -132,7 +133,7 @@ public final class EntityDefinitions { public static final EntityDefinition POTION; public static final EntityDefinition PUFFERFISH; public static final EntityDefinition RABBIT; - public static final EntityDefinition RAVAGER; + public static final EntityDefinition RAVAGER; public static final EntityDefinition SALMON; public static final EntityDefinition SHEEP; public static final EntityDefinition SHULKER; @@ -745,9 +746,9 @@ public final class EntityDefinitions { .type(EntityType.PILLAGER) .height(1.8f).width(0.6f) .offset(1.62f) - .addTranslator(null) // Charging; doesn't have an equivalent on Bedrock //TODO check + .addTranslator(MetadataType.BOOLEAN, PillagerEntity::setChargingCrossbow) .build(); - RAVAGER = EntityDefinition.inherited(raidParticipantEntityBase.factory(), raidParticipantEntityBase) + RAVAGER = EntityDefinition.inherited(RavagerEntity::new, raidParticipantEntityBase) .type(EntityType.RAVAGER) .height(1.9f).width(1.2f) .build(); diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/LivingEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/LivingEntity.java index d27fa3cad..499084555 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/LivingEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/LivingEntity.java @@ -352,6 +352,15 @@ public class LivingEntity extends Entity { session.sendUpstreamPacket(offHandPacket); } + /** + * Called when a SWING_ARM animation packet is received + * + * @return true if an ATTACK_START event should be used instead + */ + public boolean useArmSwingAttack() { + return false; + } + /** * Attributes are properties of an entity that are generally more runtime-based instead of permanent properties. * Movement speed, current attack damage with a weapon, current knockback resistance. diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/HoglinEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/HoglinEntity.java index 46cafad02..74c937417 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/HoglinEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/HoglinEntity.java @@ -27,6 +27,7 @@ package org.geysermc.geyser.entity.type.living.animal; import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.math.vector.Vector3f; +import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.session.GeyserSession; @@ -40,6 +41,8 @@ public class HoglinEntity extends AnimalEntity { public HoglinEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) { super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw); + dirtyMetadata.put(EntityDataTypes.TARGET_EID, session.getPlayerEntity().getGeyserId()); + setFlag(EntityFlag.SHAKING, isShaking()); } public void setImmuneToZombification(BooleanEntityMetadata entityMetadata) { @@ -68,4 +71,9 @@ public class HoglinEntity extends AnimalEntity { protected boolean isEnemy() { return true; } + + @Override + public boolean useArmSwingAttack() { + return true; + } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/AbstractSkeletonEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/AbstractSkeletonEntity.java index 3fbdda245..d08fff06a 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/AbstractSkeletonEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/AbstractSkeletonEntity.java @@ -26,7 +26,9 @@ package org.geysermc.geyser.entity.type.living.monster; import org.cloudburstmc.math.vector.Vector3f; +import org.cloudburstmc.protocol.bedrock.data.definitions.ItemDefinition; import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; +import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata; @@ -45,5 +47,17 @@ public class AbstractSkeletonEntity extends MonsterEntity { byte xd = entityMetadata.getPrimitiveValue(); // A bit of a loophole so the hands get raised - set the target ID to its own ID dirtyMetadata.put(EntityDataTypes.TARGET_EID, ((xd & 4) == 4) ? geyserId : 0); + + if ((xd & 4) == 4) { + ItemDefinition bow = session.getItemMappings().getStoredItems().bow().getBedrockDefinition(); + setFlag(EntityFlag.FACING_TARGET_TO_RANGE_ATTACK, this.hand.getDefinition() == bow || this.offhand.getDefinition() == bow); + } else { + setFlag(EntityFlag.FACING_TARGET_TO_RANGE_ATTACK, false); + } + } + + @Override + public boolean useArmSwingAttack() { + return true; } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/BasePiglinEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/BasePiglinEntity.java index 09bd28cd0..9258cd3b8 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/BasePiglinEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/BasePiglinEntity.java @@ -26,10 +26,13 @@ package org.geysermc.geyser.entity.type.living.monster; import org.cloudburstmc.math.vector.Vector3f; +import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; +import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata; +import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType; import java.util.UUID; @@ -38,6 +41,16 @@ public class BasePiglinEntity extends MonsterEntity { public BasePiglinEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) { super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw); + // Both TARGET_EID and BLOCK are needed for melee attack animation + dirtyMetadata.put(EntityDataTypes.BLOCK, session.getBlockMappings().getDefinition(1)); + setFlag(EntityFlag.SHAKING, isShaking()); + } + + @Override + public void setMobFlags(ByteEntityMetadata entityMetadata) { + super.setMobFlags(entityMetadata); + byte xd = entityMetadata.getPrimitiveValue(); + dirtyMetadata.put(EntityDataTypes.TARGET_EID, (xd & 4) == 4 ? session.getPlayerEntity().getGeyserId() : 0); } public void setImmuneToZombification(BooleanEntityMetadata entityMetadata) { @@ -50,4 +63,9 @@ public class BasePiglinEntity extends MonsterEntity { protected boolean isShaking() { return (!isImmuneToZombification && !session.getDimensionType().piglinSafe()) || super.isShaking(); } + + @Override + public boolean useArmSwingAttack() { + return true; + } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/PiglinEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/PiglinEntity.java index f0f01272f..19b6d8e69 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/PiglinEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/PiglinEntity.java @@ -27,16 +27,22 @@ package org.geysermc.geyser.entity.type.living.monster; import org.checkerframework.checker.nullness.qual.NonNull; import org.cloudburstmc.math.vector.Vector3f; +import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; +import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerId; +import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; +import org.cloudburstmc.protocol.bedrock.packet.MobEquipmentPacket; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.item.Items; +import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.tags.ItemTag; import org.geysermc.geyser.util.InteractionResult; import org.geysermc.geyser.util.InteractiveTag; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand; +import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack; import java.util.UUID; @@ -55,13 +61,61 @@ public class PiglinEntity extends BasePiglinEntity { } public void setChargingCrossbow(BooleanEntityMetadata entityMetadata) { - setFlag(EntityFlag.CHARGING, entityMetadata.getPrimitiveValue()); + boolean charging = entityMetadata.getPrimitiveValue(); + setFlag(EntityFlag.CHARGING, charging); + dirtyMetadata.put(EntityDataTypes.CHARGE_AMOUNT, charging ? (byte) 64 : (byte) 0); // TODO: gradually increase } public void setDancing(BooleanEntityMetadata entityMetadata) { setFlag(EntityFlag.DANCING, entityMetadata.getPrimitiveValue()); } + @Override + public void setHand(ItemStack stack) { + ItemMapping crossbow = session.getItemMappings().getStoredItems().crossbow(); + boolean toCrossbow = stack != null && stack.getId() == crossbow.getJavaItem().javaId(); + + if (toCrossbow ^ this.hand.getDefinition() == crossbow.getBedrockDefinition()) { // If switching to/from crossbow + dirtyMetadata.put(EntityDataTypes.BLOCK, session.getBlockMappings().getDefinition(toCrossbow ? 0 : 1)); + dirtyMetadata.put(EntityDataTypes.CHARGE_AMOUNT, (byte) 0); + setFlag(EntityFlag.CHARGED, false); + setFlag(EntityFlag.USING_ITEM, false); + updateBedrockMetadata(); + + if (this.hand.isValid()) { + MobEquipmentPacket mobEquipmentPacket = new MobEquipmentPacket(); + mobEquipmentPacket.setRuntimeEntityId(geyserId); + mobEquipmentPacket.setContainerId(ContainerId.INVENTORY); + mobEquipmentPacket.setInventorySlot(0); + mobEquipmentPacket.setHotbarSlot(-1); + mobEquipmentPacket.setItem(ItemData.AIR); + session.sendUpstreamPacket(mobEquipmentPacket); + } + } + + super.setHand(stack); + } + + @Override + public void updateMainHand(GeyserSession session) { + super.updateMainHand(session); + + if (this.hand.getDefinition() == session.getItemMappings().getStoredItems().crossbow().getBedrockDefinition()) { + if (this.hand.getTag() != null && this.hand.getTag().containsKey("chargedItem")) { + dirtyMetadata.put(EntityDataTypes.CHARGE_AMOUNT, Byte.MAX_VALUE); + setFlag(EntityFlag.CHARGING, false); + setFlag(EntityFlag.CHARGED, true); + setFlag(EntityFlag.USING_ITEM, true); + } else if (getFlag(EntityFlag.CHARGED)) { + dirtyMetadata.put(EntityDataTypes.CHARGE_AMOUNT, (byte) 0); + setFlag(EntityFlag.CHARGED, false); + setFlag(EntityFlag.USING_ITEM, false); + } + } + + updateBedrockMetadata(); + } + @Override public void updateOffHand(GeyserSession session) { // Check if the Piglin is holding Gold and set the ADMIRING flag accordingly so its pose updates diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/ZoglinEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/ZoglinEntity.java index 7eb950e21..206746fb9 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/ZoglinEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/ZoglinEntity.java @@ -26,6 +26,7 @@ package org.geysermc.geyser.entity.type.living.monster; import org.cloudburstmc.math.vector.Vector3f; +import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.session.GeyserSession; @@ -37,6 +38,7 @@ public class ZoglinEntity extends MonsterEntity { public ZoglinEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) { super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw); + dirtyMetadata.put(EntityDataTypes.TARGET_EID, session.getPlayerEntity().getGeyserId()); } public void setBaby(BooleanEntityMetadata entityMetadata) { @@ -64,4 +66,9 @@ public class ZoglinEntity extends MonsterEntity { protected boolean isEnemy() { return true; } + + @Override + public boolean useArmSwingAttack() { + return true; + } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/ZombieEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/ZombieEntity.java index b8351089d..b07afd742 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/ZombieEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/ZombieEntity.java @@ -57,4 +57,9 @@ public class ZombieEntity extends MonsterEntity { protected boolean isShaking() { return convertingToDrowned || super.isShaking(); } + + @Override + public boolean useArmSwingAttack() { + return true; + } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/raid/PillagerEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/raid/PillagerEntity.java index 1d2d9115b..fd7448e29 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/raid/PillagerEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/raid/PillagerEntity.java @@ -26,10 +26,13 @@ package org.geysermc.geyser.entity.type.living.monster.raid; import org.cloudburstmc.math.vector.Vector3f; +import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; +import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; import java.util.UUID; @@ -39,16 +42,22 @@ public class PillagerEntity extends AbstractIllagerEntity { super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw); } + public void setChargingCrossbow(BooleanEntityMetadata entityMetadata) { + boolean charging = entityMetadata.getPrimitiveValue(); + setFlag(EntityFlag.CHARGING, charging); + dirtyMetadata.put(EntityDataTypes.CHARGE_AMOUNT, charging ? (byte) 64 : (byte) 0); // TODO: gradually increase + } + @Override - public void updateMainHand(GeyserSession session) { //TODO - checkForCrossbow(); + public void updateMainHand(GeyserSession session) { + updateCrossbow(); super.updateMainHand(session); } @Override public void updateOffHand(GeyserSession session) { - checkForCrossbow(); + updateCrossbow(); super.updateOffHand(session); } @@ -56,12 +65,27 @@ public class PillagerEntity extends AbstractIllagerEntity { /** * Check for a crossbow in either the mainhand or offhand. If one exists, indicate that the pillager should be posing */ - protected void checkForCrossbow() { + protected void updateCrossbow() { ItemMapping crossbow = session.getItemMappings().getStoredItems().crossbow(); - boolean hasCrossbow = this.hand.getDefinition() == crossbow.getBedrockDefinition() - || this.offhand.getDefinition() == crossbow.getBedrockDefinition(); - setFlag(EntityFlag.USING_ITEM, hasCrossbow); - setFlag(EntityFlag.CHARGED, hasCrossbow); + ItemData activeCrossbow = null; + if (this.hand.getDefinition() == crossbow.getBedrockDefinition()) { + activeCrossbow = this.hand; + } else if (this.offhand.getDefinition() == crossbow.getBedrockDefinition()) { + activeCrossbow = this.offhand; + } + + if (activeCrossbow != null) { + if (activeCrossbow.getTag() != null && activeCrossbow.getTag().containsKey("chargedItem")) { + dirtyMetadata.put(EntityDataTypes.CHARGE_AMOUNT, Byte.MAX_VALUE); + setFlag(EntityFlag.CHARGING, false); + setFlag(EntityFlag.CHARGED, true); + setFlag(EntityFlag.USING_ITEM, true); + } else if (getFlag(EntityFlag.CHARGED)) { + dirtyMetadata.put(EntityDataTypes.CHARGE_AMOUNT, (byte) 0); + setFlag(EntityFlag.CHARGED, false); + setFlag(EntityFlag.USING_ITEM, false); + } + } updateBedrockMetadata(); } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/raid/RavagerEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/raid/RavagerEntity.java new file mode 100644 index 000000000..6190bae10 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/raid/RavagerEntity.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2019-2024 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.geyser.entity.type.living.monster.raid; + +import org.cloudburstmc.math.vector.Vector3f; +import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; +import org.geysermc.geyser.entity.EntityDefinition; +import org.geysermc.geyser.session.GeyserSession; + +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +public class RavagerEntity extends RaidParticipantEntity { + + public RavagerEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) { + super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw); + } + + @Override + public boolean useArmSwingAttack() { + setFlag(EntityFlag.DELAYED_ATTACK, false); + updateBedrockMetadata(); + + session.scheduleInEventLoop(() -> { + setFlag(EntityFlag.DELAYED_ATTACK, true); + updateBedrockMetadata(); + }, 75, TimeUnit.MILLISECONDS); + + return true; + } +} diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/raid/VindicatorEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/raid/VindicatorEntity.java index 04a58addd..a2557e75a 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/raid/VindicatorEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/raid/VindicatorEntity.java @@ -26,6 +26,7 @@ package org.geysermc.geyser.entity.type.living.monster.raid; import org.cloudburstmc.math.vector.Vector3f; +import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.session.GeyserSession; @@ -37,6 +38,7 @@ public class VindicatorEntity extends AbstractIllagerEntity { public VindicatorEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) { super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw); + dirtyMetadata.put(EntityDataTypes.TARGET_EID, session.getPlayerEntity().getGeyserId()); } @Override @@ -46,4 +48,9 @@ public class VindicatorEntity extends AbstractIllagerEntity { byte xd = entityMetadata.getPrimitiveValue(); setFlag(EntityFlag.ANGRY, (xd & 4) == 4); } + + @Override + public boolean useArmSwingAttack() { + return true; + } } diff --git a/core/src/main/java/org/geysermc/geyser/inventory/item/StoredItemMappings.java b/core/src/main/java/org/geysermc/geyser/inventory/item/StoredItemMappings.java index 0305df685..05f6ba6cc 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/item/StoredItemMappings.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/item/StoredItemMappings.java @@ -42,6 +42,7 @@ import java.util.Map; public class StoredItemMappings { private final ItemMapping banner; private final ItemMapping barrier; + private final ItemMapping bow; private final ItemMapping compass; private final ItemMapping crossbow; private final ItemMapping egg; @@ -57,6 +58,7 @@ public class StoredItemMappings { public StoredItemMappings(Map itemMappings) { this.banner = load(itemMappings, Items.WHITE_BANNER); // As of 1.17.10, all banners have the same Bedrock ID this.barrier = load(itemMappings, Items.BARRIER); + this.bow = load(itemMappings, Items.BOW); this.compass = load(itemMappings, Items.COMPASS); this.crossbow = load(itemMappings, Items.CROSSBOW); this.egg = load(itemMappings, Items.EGG); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaAnimateTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaAnimateTranslator.java index 8bd4bad10..bf6dfe684 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaAnimateTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaAnimateTranslator.java @@ -26,10 +26,13 @@ package org.geysermc.geyser.translator.protocol.java.entity; import org.cloudburstmc.math.vector.Vector3f; +import org.cloudburstmc.protocol.bedrock.data.entity.EntityEventType; import org.cloudburstmc.protocol.bedrock.packet.AnimateEntityPacket; import org.cloudburstmc.protocol.bedrock.packet.AnimatePacket; +import org.cloudburstmc.protocol.bedrock.packet.EntityEventPacket; import org.cloudburstmc.protocol.bedrock.packet.SpawnParticleEffectPacket; import org.geysermc.geyser.entity.type.Entity; +import org.geysermc.geyser.entity.type.LivingEntity; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; @@ -57,6 +60,14 @@ public class JavaAnimateTranslator extends PacketTranslator { + if (entity instanceof LivingEntity livingEntity && livingEntity.useArmSwingAttack()) { + EntityEventPacket entityEventPacket = new EntityEventPacket(); + entityEventPacket.setRuntimeEntityId(entity.getGeyserId()); + entityEventPacket.setType(EntityEventType.ATTACK_START); + session.sendUpstreamPacket(entityEventPacket); + return; + } + animatePacket.setAction(AnimatePacket.Action.SWING_ARM); if (entity.getEntityId() == session.getPlayerEntity().getEntityId()) { session.activateArmAnimationTicking();