1
0
mirror of https://github.com/GeyserMC/Geyser.git synced 2026-01-04 15:31:36 +00:00

Begin updating interaction logic

This commit is contained in:
basaigh
2025-06-15 13:34:25 +01:00
parent ce155df5dd
commit 4341ab4e5e
13 changed files with 113 additions and 20 deletions

View File

@@ -45,21 +45,34 @@ import org.geysermc.geyser.api.entity.type.GeyserEntity;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.entity.GeyserDirtyMetadata;
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;
@@ -682,13 +695,25 @@ public class Entity implements GeyserEntity {
* to ensure packet parity as well as functionality parity (such as sound effect responses).
*/
public InteractionResult interact(Hand hand) {
Item itemInHand = session.getPlayerInventory().getItemInHand(hand).asItem();
if (itemInHand == Items.SHEARS) {
if (hasLeashesToDrop()) {
return InteractionResult.SUCCESS;
}
if (this instanceof MobEntity mob && mob.passengers.isEmpty() && !session.isSneaking() && canShearEquipment(mob)) {
return InteractionResult.SUCCESS;
}
}
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.
return InteractionResult.SUCCESS;
}
if (session.getPlayerInventory().getItemInHand(hand).asItem() == Items.LEAD && leashable.canBeLeashed()) {
if (session.getPlayerInventory().getItemInHand(hand).asItem() == Items.LEAD
&& !(session.getEntityCache().getEntityByGeyserId(leashable.leashHolderBedrockId()) instanceof PlayerEntity)) {
// We shall leash
return InteractionResult.SUCCESS;
}
@@ -697,6 +722,43 @@ public class Entity implements GeyserEntity {
return InteractionResult.PASS;
}
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 -> {
BoundingBox leashedBB = new BoundingBox(entity.position.toDouble(), entity.boundingBoxWidth, entity.boundingBoxHeight, entity.boundingBoxWidth);
return searchBB.checkIntersection(leashedBB);
}).map(Leashable.class::cast).toList();
boolean found = !leashedInRange.isEmpty();
if (this instanceof Leashable leashable && leashable.isLeashed()) {
found = true;
}
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

@@ -35,10 +35,10 @@ public interface Leashable {
long leashHolderBedrockId();
default boolean canBeLeashed() {
return isNotLeashed();
return true;
}
default boolean isNotLeashed() {
return leashHolderBedrockId() == -1L;
default boolean isLeashed() {
return leashHolderBedrockId() != -1L;
}
}

View File

@@ -73,6 +73,7 @@ import org.geysermc.mcprotocollib.protocol.data.game.level.particle.ParticleType
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumMap;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
@@ -80,6 +81,8 @@ import java.util.UUID;
@Getter
@Setter
public class LivingEntity extends Entity {
protected EnumMap<EquipmentSlot, ItemStack> equipment = new EnumMap<>(EquipmentSlot.class);
protected ItemData helmet = ItemData.AIR;
protected ItemData chestplate = ItemData.AIR;
protected ItemData leggings = ItemData.AIR;
@@ -117,26 +120,32 @@ public class LivingEntity extends Entity {
}
public void setHelmet(ItemStack stack) {
this.equipment.put(EquipmentSlot.HELMET, stack);
this.helmet = ItemTranslator.translateToBedrock(session, stack);
}
public void setChestplate(ItemStack stack) {
this.equipment.put(EquipmentSlot.CHESTPLATE, stack);
this.chestplate = ItemTranslator.translateToBedrock(session, stack);
}
public void setLeggings(ItemStack stack) {
this.equipment.put(EquipmentSlot.LEGGINGS, stack);
this.leggings = ItemTranslator.translateToBedrock(session, stack);
}
public void setBoots(ItemStack stack) {
this.equipment.put(EquipmentSlot.BOOTS, stack);
this.boots = ItemTranslator.translateToBedrock(session, stack);
}
public void setBody(ItemStack stack) {
this.equipment.put(EquipmentSlot.BODY, stack);
this.body = ItemTranslator.translateToBedrock(session, stack);
}
public void setSaddle(@Nullable ItemStack stack) {
this.equipment.put(EquipmentSlot.SADDLE, stack);
this.saddle = ItemTranslator.translateToBedrock(session, stack);
boolean saddled = false;
@@ -153,11 +162,11 @@ public class LivingEntity extends Entity {
}
public void setHand(ItemStack stack) {
this.hand = ItemTranslator.translateToBedrock(session, stack);
this.equipment.put(EquipmentSlot.MAIN_HAND, stack);
}
public void setOffhand(ItemStack stack) {
this.offhand = ItemTranslator.translateToBedrock(session, stack);
this.equipment.put(EquipmentSlot.OFF_HAND, stack);
}
protected void updateSaddled(boolean saddled) {
@@ -172,9 +181,13 @@ public class LivingEntity extends Entity {
}
public void switchHands() {
ItemData offhand = this.offhand;
ItemStack 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);
ItemData bedrockOffhand = this.offhand;
this.offhand = this.hand;
this.hand = offhand;
this.hand = bedrockOffhand;
}
@Override

View File

@@ -136,7 +136,7 @@ public class MobEntity extends LivingEntity implements Leashable {
@Override
public boolean canBeLeashed() {
return isNotLeashed() && !isEnemy();
return !isEnemy();
}
@Override

View File

@@ -123,7 +123,7 @@ public class SquidEntity extends AgeableWaterEntity implements Tickable {
@Override
public boolean canBeLeashed() {
return isNotLeashed();
return true;
}
private void checkInWater() {

View File

@@ -37,6 +37,7 @@ import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.geyser.session.cache.tags.Tag;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.InteractiveTag;
import org.geysermc.mcprotocollib.protocol.data.game.entity.EquipmentSlot;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
@@ -82,15 +83,15 @@ public class HappyGhastEntity extends AnimalEntity {
} else {
if (!itemInHand.isEmpty()) {
if (session.getTagCache().is(ItemTag.HARNESSES, itemInHand)) {
if (!this.body.isValid()) {
if (this.equipment.get(EquipmentSlot.BODY) == null) {
// Harnesses the ghast
return InteractiveTag.HARNESS_HAPPY_GHAST;
return InteractiveTag.EQUIP_HARNESS;
}
}
// TODO: Handle shearing the harness off
}
if (this.body.isValid() && !session.isSneaking()) {
if (this.equipment.get(EquipmentSlot.BODY) != null && !session.isSneaking()) {
// Rides happy ghast
return InteractiveTag.RIDE_HORSE;
} else {
@@ -107,7 +108,7 @@ public class HappyGhastEntity extends AnimalEntity {
} else {
if (!itemInHand.isEmpty()) {
if (session.getTagCache().is(ItemTag.HARNESSES, itemInHand)) {
if (!this.body.isValid()) {
if (this.equipment.get(EquipmentSlot.BODY) == null) {
// Harnesses the ghast
return InteractionResult.SUCCESS;
}
@@ -115,7 +116,7 @@ public class HappyGhastEntity extends AnimalEntity {
// TODO: Handle shearing the harness off
}
if (this.body.isValid() && !session.isSneaking()) {
if (this.equipment.get(EquipmentSlot.BODY) == null && !session.isSneaking()) {
// Rides happy ghast
return InteractionResult.SUCCESS;
} else {

View File

@@ -66,7 +66,7 @@ public class HoglinEntity extends AnimalEntity {
@Override
public boolean canBeLeashed() {
return isNotLeashed();
return true;
}
@Override

View File

@@ -85,6 +85,6 @@ public abstract class TameableEntity extends AnimalEntity {
@Override
public boolean canBeLeashed() {
return isNotLeashed();
return true;
}
}

View File

@@ -139,7 +139,7 @@ public class WolfEntity extends TameableEntity implements VariantIntHolder {
@Override
public boolean canBeLeashed() {
return !getFlag(EntityFlag.ANGRY) && super.canBeLeashed();
return !getFlag(EntityFlag.ANGRY);
}
@NonNull

View File

@@ -59,7 +59,7 @@ public class ZoglinEntity extends MonsterEntity {
@Override
public boolean canBeLeashed() {
return isNotLeashed();
return true;
}
@Override

View File

@@ -161,6 +161,8 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity {
// Since we re-use player entities: Clear flags, held item, etc
this.resetMetadata();
this.nametag = username;
this.equipment.clear();
this.hand = ItemData.AIR;
this.offhand = ItemData.AIR;
this.boots = ItemData.AIR;

View File

@@ -43,6 +43,15 @@ public class BoundingBox implements Cloneable {
private double sizeY;
private double sizeZ;
public BoundingBox(Vector3d position, double sizeX, double sizeY, double sizeZ) {
this.middleX = position.getX();
this.middleY = position.getY();
this.middleZ = position.getZ();
this.sizeX = sizeX;
this.sizeY = sizeY;
this.sizeZ = sizeZ;
}
public void translate(double x, double y, double z) {
middleX += x;
middleY += y;
@@ -87,6 +96,10 @@ public class BoundingBox implements Cloneable {
return checkIntersection(offset.getX(), offset.getY(), offset.getZ(), otherBox);
}
public boolean checkIntersection(BoundingBox otherBox) {
return checkIntersection(0, 0, 0, otherBox);
}
public Vector3d getMin() {
double x = middleX - sizeX / 2;
double y = middleY - sizeY / 2;

View File

@@ -57,6 +57,7 @@ public enum InteractiveTag {
CREATE_MAP("createMap"),
TAKE_PICTURE("takepicture"),
SADDLE,
REMOVE_SADDLE("removesaddle"),
MOUNT,
BOOST,
WRITE,
@@ -74,7 +75,8 @@ public enum InteractiveTag {
EQUIP_WOLF_ARMOR("equipwolfarmor"),
REMOVE_WOLF_ARMOR("removewolfarmor"),
REPAIR_WOLF_ARMOR("repairwolfarmor"),
HARNESS_HAPPY_GHAST("equipharness");
EQUIP_HARNESS("equipharness"),
REMOVE_HARNESS("removeharness");
/**
* The full string that should be passed on to the client.