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 1a6da0d91..507b726e0 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 @@ -51,6 +51,7 @@ import org.geysermc.geyser.scoreboard.Team; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.item.ItemTranslator; import org.geysermc.geyser.util.AttributeUtils; +import org.geysermc.geyser.util.EntityUtils; import org.geysermc.geyser.util.InteractionResult; import org.geysermc.geyser.util.MathUtils; import org.geysermc.mcprotocollib.protocol.data.game.entity.EquipmentSlot; @@ -537,6 +538,32 @@ public class LivingEntity extends Entity { } } + protected boolean hasBodyArmor() { + return this.hasValidEquippableItemForSlot(EquipmentSlot.BODY); + } + + private boolean hasValidEquippableItemForSlot(EquipmentSlot slot) { + // MojMap LivingEntity#hasItemInSlot + GeyserItemStack itemInSlot = equipment.get(slot); + if (itemInSlot != null) { + // MojMap LivingEntity#isEquippableInSlot + Equippable equippable = itemInSlot.getComponent(DataComponentTypes.EQUIPPABLE); + if (equippable != null) { + return slot == equippable.slot() && + canUseSlot(slot) && + EntityUtils.equipmentUsableByEntity(session, equippable, this.definition.entityType()); + } else { + return slot == EquipmentSlot.MAIN_HAND && canUseSlot(EquipmentSlot.MAIN_HAND); + } + } + + return false; + } + + protected boolean canUseSlot(EquipmentSlot slot) { + return true; + } + /** * Calculates the complete attribute value to send to Bedrock. Will be overriden if attributes need to be cached. */ diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/HappyGhastEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/HappyGhastEntity.java index 5c2d9d474..9babe06f3 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/HappyGhastEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/HappyGhastEntity.java @@ -30,8 +30,10 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.math.TrigMath; import org.cloudburstmc.math.vector.Vector2f; import org.cloudburstmc.math.vector.Vector3f; +import org.cloudburstmc.protocol.bedrock.data.AttributeData; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.entity.EntityDefinition; +import org.geysermc.geyser.entity.attribute.GeyserAttributeType; import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; import org.geysermc.geyser.entity.vehicle.ClientVehicle; @@ -42,12 +44,16 @@ import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.tags.ItemTag; import org.geysermc.geyser.session.cache.tags.Tag; +import org.geysermc.geyser.util.AttributeUtils; 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.attribute.Attribute; +import org.geysermc.mcprotocollib.protocol.data.game.entity.attribute.AttributeType; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand; +import java.util.List; import java.util.UUID; public class HappyGhastEntity extends AnimalEntity implements ClientVehicle { @@ -180,12 +186,7 @@ public class HappyGhastEntity extends AnimalEntity implements ClientVehicle { @Override public boolean isClientControlled() { - // TODO proper check, just lazy - if (body == null) { - return false; - } - // TODO must have ai check - if (staysStill) { + if (!hasBodyArmor() || getFlag(EntityFlag.NO_AI) || staysStill) { return false; } @@ -195,4 +196,25 @@ public class HappyGhastEntity extends AnimalEntity implements ClientVehicle { private Entity getFirstPassenger() { return passengers.isEmpty() ? null : passengers.get(0); } + + @Override + protected void updateAttribute(Attribute javaAttribute, List newAttributes) { + super.updateAttribute(javaAttribute, newAttributes); + if (javaAttribute.getType() instanceof AttributeType.Builtin type) { + switch (type) { + case FLYING_SPEED -> { + AttributeData attributeData = calculateAttribute(javaAttribute, GeyserAttributeType.FLYING_SPEED); + vehicleComponent.setFlyingSpeed(attributeData.getValue()); + } + case CAMERA_DISTANCE -> { + vehicleComponent.setCameraDistance((float) AttributeUtils.calculateValue(javaAttribute)); + } + } + } + } + + @Override + protected boolean canUseSlot(EquipmentSlot slot) { + return slot != EquipmentSlot.BODY ? super.canUseSlot(slot) : this.isAlive() && !this.isBaby(); + } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java index a2febc068..36a126687 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java @@ -47,6 +47,7 @@ import org.geysermc.geyser.session.cache.tags.Tag; import org.geysermc.geyser.util.EntityUtils; 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.metadata.type.IntEntityMetadata; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand; @@ -195,4 +196,9 @@ public class StriderEntity extends AnimalEntity implements Tickable, ClientVehic public boolean canWalkOnLava() { return true; } + + @Override + protected boolean canUseSlot(EquipmentSlot slot) { + return slot != EquipmentSlot.SADDLE ? super.canUseSlot(slot) : this.isAlive() && !this.isBaby(); + } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/farm/PigEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/farm/PigEntity.java index 60b549a60..7ae672bcc 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/farm/PigEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/farm/PigEntity.java @@ -48,6 +48,7 @@ import org.geysermc.geyser.session.cache.tags.Tag; import org.geysermc.geyser.util.EntityUtils; 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.IntEntityMetadata; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand; @@ -154,4 +155,9 @@ public class PigEntity extends TemperatureVariantAnimal implements Tickable, Cli public JavaRegistryKey variantRegistry() { return JavaRegistries.PIG_VARIANT; } + + @Override + protected boolean canUseSlot(EquipmentSlot slot) { + return slot != EquipmentSlot.SADDLE ? super.canUseSlot(slot) : this.isAlive() && !this.isBaby(); + } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/AbstractHorseEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/AbstractHorseEntity.java index 7b6184579..8b0e77c73 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/AbstractHorseEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/AbstractHorseEntity.java @@ -45,6 +45,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.ByteEntityMetadata; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand; @@ -286,4 +287,13 @@ public class AbstractHorseEntity extends AnimalEntity { return InteractionResult.SUCCESS; } } + + @Override + protected boolean canUseSlot(EquipmentSlot slot) { + if (slot != EquipmentSlot.SADDLE) { + return super.canUseSlot(slot); + } else { + return isAlive() && !isBaby() && getFlag(EntityFlag.TAMED); + } + } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/HorseEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/HorseEntity.java index b8a9a8f28..478f89aeb 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/HorseEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/HorseEntity.java @@ -29,6 +29,7 @@ import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.mcprotocollib.protocol.data.game.entity.EquipmentSlot; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata; import java.util.UUID; @@ -44,4 +45,9 @@ public class HorseEntity extends AbstractHorseEntity { dirtyMetadata.put(EntityDataTypes.VARIANT, value & 255); dirtyMetadata.put(EntityDataTypes.MARK_VARIANT, (value >> 8) % 5); } -} \ No newline at end of file + + @Override + protected boolean canUseSlot(EquipmentSlot slot) { + return true; + } +} diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/LlamaEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/LlamaEntity.java index d27a1fff3..6ef1cd57a 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/LlamaEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/horse/LlamaEntity.java @@ -35,6 +35,7 @@ import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.tags.ItemTag; import org.geysermc.geyser.session.cache.tags.Tag; import org.geysermc.geyser.util.MathUtils; +import org.geysermc.mcprotocollib.protocol.data.game.entity.EquipmentSlot; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata; import java.util.UUID; @@ -61,4 +62,9 @@ public class LlamaEntity extends ChestedHorseEntity { protected @Nullable Tag getFoodTag() { return ItemTag.LLAMA_FOOD; } + + @Override + protected boolean canUseSlot(EquipmentSlot slot) { + return true; + } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/ClientVehicle.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/ClientVehicle.java index e7617e109..c0480ee90 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/ClientVehicle.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/ClientVehicle.java @@ -31,7 +31,7 @@ import org.cloudburstmc.math.vector.Vector3f; public interface ClientVehicle { VehicleComponent getVehicleComponent(); - // LivingEntity#getRiddenInput + // MojMap LivingEntity#getRiddenInput Vector3f getRiddenInput(Vector2f input); // MojMap LivingEntity#getRiddenSpeed diff --git a/core/src/main/java/org/geysermc/geyser/entity/vehicle/HappyGhastVehicleComponent.java b/core/src/main/java/org/geysermc/geyser/entity/vehicle/HappyGhastVehicleComponent.java index 0f666738a..88045064a 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/vehicle/HappyGhastVehicleComponent.java +++ b/core/src/main/java/org/geysermc/geyser/entity/vehicle/HappyGhastVehicleComponent.java @@ -27,25 +27,31 @@ package org.geysermc.geyser.entity.vehicle; import lombok.Getter; import lombok.Setter; -import org.cloudburstmc.math.vector.Vector2f; import org.cloudburstmc.math.vector.Vector3d; -import org.cloudburstmc.math.vector.Vector3f; +import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.geysermc.erosion.util.BlockPositionIterator; import org.geysermc.geyser.entity.type.living.animal.HappyGhastEntity; +import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; import org.geysermc.geyser.level.block.Blocks; import org.geysermc.geyser.level.block.Fluid; import org.geysermc.geyser.level.block.type.BlockState; import org.geysermc.geyser.level.physics.BoundingBox; import org.geysermc.geyser.util.MathUtils; +import org.geysermc.mcprotocollib.protocol.data.game.entity.attribute.AttributeType; +@Setter +@Getter public class HappyGhastVehicleComponent extends VehicleComponent { - @Getter @Setter private float flyingSpeed; + private float cameraDistance; public HappyGhastVehicleComponent(HappyGhastEntity vehicle, float stepHeight) { super(vehicle, stepHeight); - flyingSpeed = 0.05f; // Happy Ghast has different default + // Happy Ghast has different defaults + flyingSpeed = 0.05f; + moveSpeed = 0.05f; + cameraDistance = 8.0f; } @Override @@ -54,6 +60,26 @@ public class HappyGhastVehicleComponent extends VehicleComponent 0.5f; case EMPTY -> 0.91f; }; + // HappyGhast#travel travel(ctx, flyingSpeed * 5.0f / 3.0f); vehicle.setMotion(vehicle.getMotion().mul(drag)); } @@ -98,7 +125,4 @@ public class HappyGhastVehicleComponent extends VehicleComponent { * * @return true if there was a horizontal collision */ - // Mojmap: LivingEntity#moveRelative + // Mojmap: LivingEntity#moveRelative / LivingEntity#move protected boolean travel(VehicleContext ctx, float speed) { Vector3f motion = vehicle.getMotion(); diff --git a/core/src/main/java/org/geysermc/geyser/util/EntityUtils.java b/core/src/main/java/org/geysermc/geyser/util/EntityUtils.java index 9cdcbed9f..9126b424d 100644 --- a/core/src/main/java/org/geysermc/geyser/util/EntityUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/EntityUtils.java @@ -44,11 +44,14 @@ import org.geysermc.geyser.entity.type.living.animal.horse.CamelEntity; import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.item.Items; import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.session.cache.registry.JavaRegistries; +import org.geysermc.geyser.session.cache.tags.GeyserHolderSet; import org.geysermc.geyser.text.MinecraftLocale; import org.geysermc.mcprotocollib.protocol.data.game.entity.Effect; 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.component.Equippable; import java.util.Locale; @@ -283,23 +286,12 @@ public final class EntityUtils { passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_LOCK_RIDER_ROTATION_DEGREES, 90f); passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_HAS_ROTATION, true); passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_ROTATION_OFFSET_DEGREES, -90f); - } else if (mount instanceof HappyGhastEntity) { - passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_LOCK_RIDER_ROTATION, false); - passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_LOCK_RIDER_ROTATION_DEGREES, 181f); - passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_THIRD_PERSON_CAMERA_RADIUS, 8f); - passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_CAMERA_RELAX_DISTANCE_SMOOTHING, 6f); - - passenger.getDirtyMetadata().put(EntityDataTypes.CONTROLLING_RIDER_SEAT_INDEX, (byte) 0); } } else { passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_LOCK_RIDER_ROTATION, false); passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_LOCK_RIDER_ROTATION_DEGREES, 0f); passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_HAS_ROTATION, false); passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_ROTATION_OFFSET_DEGREES, 0f); - // TODO what are defaults here??? - passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_THIRD_PERSON_CAMERA_RADIUS, 8f); - passenger.getDirtyMetadata().put(EntityDataTypes.SEAT_CAMERA_RELAX_DISTANCE_SMOOTHING, 6f); - passenger.getDirtyMetadata().put(EntityDataTypes.CONTROLLING_RIDER_SEAT_INDEX, (byte) 0); } } @@ -363,6 +355,15 @@ public final class EntityUtils { return translatedEntityName("minecraft", typeName, session); } + public static boolean equipmentUsableByEntity(GeyserSession session, Equippable equippable, EntityType entity) { + if (equippable.allowedEntities() == null) { + return true; + } + + GeyserHolderSet holderSet = GeyserHolderSet.fromHolderSet(JavaRegistries.ENTITY_TYPE, equippable.allowedEntities()); + return session.getTagCache().is(holderSet, entity); + } + private EntityUtils() { } }