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:
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -136,7 +136,7 @@ public class MobEntity extends LivingEntity implements Leashable {
|
||||
|
||||
@Override
|
||||
public boolean canBeLeashed() {
|
||||
return isNotLeashed() && !isEnemy();
|
||||
return !isEnemy();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -123,7 +123,7 @@ public class SquidEntity extends AgeableWaterEntity implements Tickable {
|
||||
|
||||
@Override
|
||||
public boolean canBeLeashed() {
|
||||
return isNotLeashed();
|
||||
return true;
|
||||
}
|
||||
|
||||
private void checkInWater() {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -66,7 +66,7 @@ public class HoglinEntity extends AnimalEntity {
|
||||
|
||||
@Override
|
||||
public boolean canBeLeashed() {
|
||||
return isNotLeashed();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -85,6 +85,6 @@ public abstract class TameableEntity extends AnimalEntity {
|
||||
|
||||
@Override
|
||||
public boolean canBeLeashed() {
|
||||
return isNotLeashed();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -59,7 +59,7 @@ public class ZoglinEntity extends MonsterEntity {
|
||||
|
||||
@Override
|
||||
public boolean canBeLeashed() {
|
||||
return isNotLeashed();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user