1
0
mirror of https://github.com/GeyserMC/Geyser.git synced 2025-12-29 11:49:16 +00:00

Finish (?) entity interactions

This commit is contained in:
Eclipse
2025-06-17 18:58:00 +00:00
parent c29b20d23a
commit ecf2bfb33c
9 changed files with 80 additions and 85 deletions

View File

@@ -48,31 +48,22 @@ import org.geysermc.geyser.entity.properties.GeyserEntityPropertyManager;
import org.geysermc.geyser.entity.type.living.MobEntity;
import org.geysermc.geyser.entity.type.player.PlayerEntity;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.item.enchantment.EnchantmentComponent;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.level.physics.BoundingBox;
import org.geysermc.geyser.registry.Registries;
import org.geysermc.geyser.scoreboard.Team;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.text.MessageTranslator;
import org.geysermc.geyser.util.EntityUtils;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.InteractiveTag;
import org.geysermc.geyser.util.ItemUtils;
import org.geysermc.geyser.util.MathUtils;
import org.geysermc.mcprotocollib.protocol.data.game.entity.EquipmentSlot;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.Pose;
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.metadata.type.IntEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentTypes;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.Equippable;
import java.util.Collections;
import java.util.EnumSet;
@@ -701,12 +692,10 @@ public class Entity implements GeyserEntity {
return InteractionResult.SUCCESS;
}
if (this instanceof MobEntity mob && mob.passengers.isEmpty() && !session.isSneaking() && canShearEquipment(mob)) {
if (this instanceof MobEntity mob && !session.isSneaking() && mob.canShearEquipment()) {
return InteractionResult.SUCCESS;
}
}
if (isAlive() && this instanceof Leashable leashable) {
} else if (isAlive() && this instanceof Leashable leashable) {
if (leashable.leashHolderBedrockId() == session.getPlayerEntity().getGeyserId()) {
// Note this might also update client side (a theoretical Geyser/client desync and Java parity issue).
// Has yet to be an issue though, as of Java 1.21.
@@ -725,7 +714,7 @@ public class Entity implements GeyserEntity {
public boolean hasLeashesToDrop() {
BoundingBox searchBB = new BoundingBox(position.getX(), position.getY(), position.getZ(), 32, 32, 32);
List<Leashable> leashedInRange = session.getEntityCache().getEntities().values().stream()
.filter(entity -> entity instanceof Leashable leashablex && leashablex.leashHolderBedrockId() == this.getEntityId())
.filter(entity -> entity instanceof Leashable leashablex && leashablex.leashHolderBedrockId() == this.getGeyserId())
.filter(entity -> {
BoundingBox leashedBB = new BoundingBox(entity.position.toDouble(), entity.boundingBoxWidth, entity.boundingBoxHeight, entity.boundingBoxWidth);
return searchBB.checkIntersection(leashedBB);
@@ -739,26 +728,6 @@ public class Entity implements GeyserEntity {
return found;
}
public boolean canShearEquipment(MobEntity mob) {
for (EquipmentSlot equipmentSlot : EquipmentSlot.values()) {
ItemStack equipment = mob.equipment.get(equipmentSlot);
if (equipment == null) continue;
Item item = Registries.JAVA_ITEMS.get(equipment.getId());
if (item == null) continue;
DataComponents components = item.gatherComponents(equipment.getDataComponentsPatch());
Equippable equippable = components.get(DataComponentTypes.EQUIPPABLE);
if (equippable != null && equippable.canBeSheared()) {
if (!ItemUtils.hasEffect(session, equipment, EnchantmentComponent.PREVENT_ARMOR_CHANGE) || session.getGameMode() == GameMode.CREATIVE) {
return true;
}
}
}
return false;
}
/**
* Simulates interacting with this entity at a specific click point. As of Java Edition 1.18.1, this is only used for armor stands.
*/

View File

@@ -44,8 +44,6 @@ import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
import org.geysermc.geyser.entity.vehicle.ClientVehicle;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.registry.Registries;
import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.scoreboard.Team;
import org.geysermc.geyser.session.GeyserSession;
@@ -63,9 +61,7 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.FloatE
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ObjectEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentTypes;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.Equippable;
import org.geysermc.mcprotocollib.protocol.data.game.level.particle.ColorParticleData;
import org.geysermc.mcprotocollib.protocol.data.game.level.particle.Particle;
@@ -81,7 +77,7 @@ import java.util.UUID;
@Getter
@Setter
public class LivingEntity extends Entity {
protected EnumMap<EquipmentSlot, ItemStack> equipment = new EnumMap<>(EquipmentSlot.class);
protected EnumMap<EquipmentSlot, GeyserItemStack> equipment = new EnumMap<>(EquipmentSlot.class);
protected ItemData helmet = ItemData.AIR;
protected ItemData chestplate = ItemData.AIR;
@@ -119,54 +115,52 @@ public class LivingEntity extends Entity {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
public void setHelmet(ItemStack stack) {
public void setHelmet(GeyserItemStack stack) {
this.equipment.put(EquipmentSlot.HELMET, stack);
this.helmet = ItemTranslator.translateToBedrock(session, stack);
}
public void setChestplate(ItemStack stack) {
public void setChestplate(GeyserItemStack stack) {
this.equipment.put(EquipmentSlot.CHESTPLATE, stack);
this.chestplate = ItemTranslator.translateToBedrock(session, stack);
}
public void setLeggings(ItemStack stack) {
public void setLeggings(GeyserItemStack stack) {
this.equipment.put(EquipmentSlot.LEGGINGS, stack);
this.leggings = ItemTranslator.translateToBedrock(session, stack);
}
public void setBoots(ItemStack stack) {
public void setBoots(GeyserItemStack stack) {
this.equipment.put(EquipmentSlot.BOOTS, stack);
this.boots = ItemTranslator.translateToBedrock(session, stack);
}
public void setBody(ItemStack stack) {
public void setBody(GeyserItemStack stack) {
this.equipment.put(EquipmentSlot.BODY, stack);
this.body = ItemTranslator.translateToBedrock(session, stack);
}
public void setSaddle(@Nullable ItemStack stack) {
public void setSaddle(GeyserItemStack stack) {
this.equipment.put(EquipmentSlot.SADDLE, stack);
this.saddle = ItemTranslator.translateToBedrock(session, stack);
boolean saddled = false;
if (stack != null) {
Item item = Registries.JAVA_ITEMS.get(stack.getId());
if (item != null) {
DataComponents components = item.gatherComponents(stack.getDataComponentsPatch());
Equippable equippable = components.get(DataComponentTypes.EQUIPPABLE);
saddled = equippable != null && equippable.slot() == EquipmentSlot.SADDLE;
}
if (!stack.isEmpty()) {
Equippable equippable = stack.getComponent(DataComponentTypes.EQUIPPABLE);
saddled = equippable != null && equippable.slot() == EquipmentSlot.SADDLE;
}
updateSaddled(saddled);
}
public void setHand(ItemStack stack) {
public void setHand(GeyserItemStack stack) {
this.equipment.put(EquipmentSlot.MAIN_HAND, stack);
this.hand = ItemTranslator.translateToBedrock(session, stack);
}
public void setOffhand(ItemStack stack) {
public void setOffhand(GeyserItemStack stack) {
this.equipment.put(EquipmentSlot.OFF_HAND, stack);
this.offhand = ItemTranslator.translateToBedrock(session, stack);
}
protected void updateSaddled(boolean saddled) {
@@ -181,7 +175,7 @@ public class LivingEntity extends Entity {
}
public void switchHands() {
ItemStack javaOffhand = this.equipment.get(EquipmentSlot.OFF_HAND);
GeyserItemStack javaOffhand = this.equipment.get(EquipmentSlot.OFF_HAND);
this.equipment.put(EquipmentSlot.OFF_HAND, this.equipment.get(EquipmentSlot.MAIN_HAND));
this.equipment.put(EquipmentSlot.MAIN_HAND, javaOffhand);

View File

@@ -36,6 +36,7 @@ import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.entity.EntityDefinitions;
import org.geysermc.geyser.entity.type.LivingEntity;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.scoreboard.Team;
import org.geysermc.geyser.session.GeyserSession;
@@ -45,7 +46,6 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetad
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.player.Hand;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import java.util.Optional;
import java.util.UUID;
@@ -266,37 +266,37 @@ public class ArmorStandEntity extends LivingEntity {
}
@Override
public void setHelmet(ItemStack helmet) {
public void setHelmet(GeyserItemStack helmet) {
super.setHelmet(helmet);
updateSecondEntityStatus(true);
}
@Override
public void setChestplate(ItemStack chestplate) {
public void setChestplate(GeyserItemStack chestplate) {
super.setChestplate(chestplate);
updateSecondEntityStatus(true);
}
@Override
public void setLeggings(ItemStack leggings) {
public void setLeggings(GeyserItemStack leggings) {
super.setLeggings(leggings);
updateSecondEntityStatus(true);
}
@Override
public void setBoots(ItemStack boots) {
public void setBoots(GeyserItemStack boots) {
super.setBoots(boots);
updateSecondEntityStatus(true);
}
@Override
public void setHand(ItemStack hand) {
public void setHand(GeyserItemStack hand) {
super.setHand(hand);
updateSecondEntityStatus(true);
}
@Override
public void setOffhand(ItemStack offHand) {
public void setOffhand(GeyserItemStack offHand) {
super.setOffhand(offHand);
updateSecondEntityStatus(true);
}

View File

@@ -34,12 +34,18 @@ import org.geysermc.geyser.entity.type.Leashable;
import org.geysermc.geyser.entity.type.LivingEntity;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.item.enchantment.EnchantmentComponent;
import org.geysermc.geyser.item.type.SpawnEggItem;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.InteractiveTag;
import org.geysermc.geyser.util.ItemUtils;
import org.geysermc.mcprotocollib.protocol.data.game.entity.EquipmentSlot;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentTypes;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.Equippable;
import java.util.UUID;
@@ -108,6 +114,26 @@ public class MobEntity extends LivingEntity implements Leashable {
}
}
public boolean canShearEquipment() {
if (!passengers.isEmpty()) {
return false;
}
for (EquipmentSlot slot : EquipmentSlot.values()) {
GeyserItemStack equipped = equipment.get(slot);
if (equipped == null || equipped.isEmpty()) continue;
Equippable equippable = equipped.getComponent(DataComponentTypes.EQUIPPABLE);
if (equippable != null && equippable.canBeSheared()) {
if (!ItemUtils.hasEffect(session, equipped, EnchantmentComponent.PREVENT_ARMOR_CHANGE) || session.getGameMode() == GameMode.CREATIVE) {
return true;
}
}
}
return false;
}
private InteractionResult checkPriorityInteractions(GeyserItemStack itemInHand) {
if (itemInHand.asItem() == Items.NAME_TAG) {
InteractionResult result = checkInteractWithNameTag(itemInHand);

View File

@@ -51,7 +51,6 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEn
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentTypes;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.HolderSet;
@@ -130,11 +129,10 @@ public class WolfEntity extends TameableEntity implements VariantIntHolder {
}
@Override
public void setBody(ItemStack stack) {
public void setBody(GeyserItemStack stack) {
super.setBody(stack);
isCurseOfBinding = ItemUtils.hasEffect(session, stack, EnchantmentComponent.PREVENT_ARMOR_CHANGE);
// Not using ItemStack#getDataComponents as that wouldn't include default item components
repairableItems = GeyserItemStack.from(stack).getComponent(DataComponentTypes.REPAIRABLE);
repairableItems = stack.getComponent(DataComponentTypes.REPAIRABLE);
}
@Override

View File

@@ -42,7 +42,6 @@ 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;
@@ -71,9 +70,9 @@ public class PiglinEntity extends BasePiglinEntity {
}
@Override
public void setHand(ItemStack stack) {
public void setHand(GeyserItemStack stack) {
ItemMapping crossbow = session.getItemMappings().getStoredItems().crossbow();
boolean toCrossbow = stack != null && stack.getId() == crossbow.getJavaItem().javaId();
boolean toCrossbow = stack != null && stack.asItem() == crossbow.getJavaItem();
if (toCrossbow ^ this.hand.getDefinition() == crossbow.getBedrockDefinition()) { // If switching to/from crossbow
dirtyMetadata.put(EntityDataTypes.BLOCK, session.getBlockMappings().getDefinition(toCrossbow ? 0 : 1));

View File

@@ -169,6 +169,22 @@ public final class ItemTranslator {
.build();
}
@NonNull
public static ItemData translateToBedrock(GeyserSession session, @NonNull GeyserItemStack stack) {
if (stack.isEmpty()) {
return ItemData.AIR;
}
ItemMapping bedrockItem = session.getItemMappings().getMapping(stack.getJavaId());
if (bedrockItem == ItemMapping.AIR) {
session.getGeyser().getLogger().debug("ItemMapping returned air: " + stack);
return ItemData.AIR;
}
return translateToBedrock(session, stack.asItem(), bedrockItem, stack.getAmount(), stack.getComponents())
.build();
}
public static ItemData.@NonNull Builder translateToBedrock(GeyserSession session, Item javaItem, ItemMapping bedrockItem, int count, @Nullable DataComponents customComponents) {
BedrockItemBuilder nbtBuilder = new BedrockItemBuilder();

View File

@@ -34,8 +34,8 @@ import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.skin.FakeHeadProvider;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.mcprotocollib.auth.GameProfile;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.Equipment;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentTypes;
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.ClientboundSetEquipmentPacket;
@@ -58,15 +58,12 @@ public class JavaSetEquipmentTranslator extends PacketTranslator<ClientboundSetE
boolean mainHandUpdated = false;
boolean offHandUpdated = false;
for (Equipment equipment : packet.getEquipment()) {
ItemStack stack = equipment.getItem();
GeyserItemStack stack = GeyserItemStack.from(equipment.getItem());
switch (equipment.getSlot()) {
case HELMET -> {
ItemStack javaItem = equipment.getItem();
if (livingEntity instanceof PlayerEntity
&& javaItem != null
&& javaItem.getId() == Items.PLAYER_HEAD.javaId()
&& javaItem.getDataComponentsPatch() != null) {
FakeHeadProvider.setHead(session, (PlayerEntity) livingEntity, GeyserItemStack.from(javaItem).getComponent(DataComponentTypes.PROFILE));
GameProfile profile = stack.getComponent(DataComponentTypes.PROFILE);
if (livingEntity instanceof PlayerEntity && stack.asItem() == Items.PLAYER_HEAD && profile != null) {
FakeHeadProvider.setHead(session, (PlayerEntity) livingEntity, profile);
} else {
FakeHeadProvider.restoreOriginalSkin(session, livingEntity);
}

View File

@@ -26,6 +26,7 @@
package org.geysermc.geyser.util;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.inventory.item.BedrockEnchantment;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.item.enchantment.Enchantment;
@@ -34,7 +35,6 @@ import org.geysermc.geyser.item.type.FishingRodItem;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.registry.JavaRegistries;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentTypes;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.ItemEnchantments;
@@ -66,16 +66,12 @@ public final class ItemUtils {
return 0;
}
public static boolean hasEffect(GeyserSession session, @Nullable ItemStack itemStack, EnchantmentComponent component) {
if (itemStack == null) {
return false;
}
DataComponents components = itemStack.getDataComponentsPatch();
if (components == null) {
public static boolean hasEffect(GeyserSession session, @Nullable GeyserItemStack itemStack, EnchantmentComponent component) {
if (itemStack == null || itemStack.isEmpty()) {
return false;
}
ItemEnchantments enchantmentData = components.get(DataComponentTypes.ENCHANTMENTS);
ItemEnchantments enchantmentData = itemStack.getComponent(DataComponentTypes.ENCHANTMENTS);
if (enchantmentData == null) {
return false;
}