diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java b/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java index 08e87dc03..cdd4fe38b 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java @@ -35,7 +35,12 @@ import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityEventType; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; -import org.cloudburstmc.protocol.bedrock.packet.*; +import org.cloudburstmc.protocol.bedrock.packet.AddEntityPacket; +import org.cloudburstmc.protocol.bedrock.packet.EntityEventPacket; +import org.cloudburstmc.protocol.bedrock.packet.MoveEntityAbsolutePacket; +import org.cloudburstmc.protocol.bedrock.packet.MoveEntityDeltaPacket; +import org.cloudburstmc.protocol.bedrock.packet.RemoveEntityPacket; +import org.cloudburstmc.protocol.bedrock.packet.SetEntityDataPacket; import org.geysermc.geyser.api.entity.type.GeyserEntity; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.entity.GeyserDirtyMetadata; @@ -55,7 +60,11 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEnt import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand; import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType; -import java.util.*; +import java.util.Collections; +import java.util.EnumSet; +import java.util.List; +import java.util.Optional; +import java.util.UUID; @Getter @Setter @@ -218,11 +227,15 @@ public class Entity implements GeyserEntity { valid = false; } - public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, boolean isOnGround) { + protected void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, boolean isOnGround) { moveRelative(relX, relY, relZ, yaw, pitch, getHeadYaw(), isOnGround); } public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) { + moveRelativeRaw(relX, relY, relZ, yaw, pitch, headYaw, isOnGround); + } + + protected void moveRelativeRaw(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) { position = Vector3f.from(position.getX() + relX, position.getY() + relY, position.getZ() + relZ); MoveEntityDeltaPacket moveEntityPacket = new MoveEntityDeltaPacket(); @@ -266,6 +279,10 @@ public class Entity implements GeyserEntity { } public void moveAbsolute(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) { + moveAbsoluteRaw(position, yaw, pitch, headYaw, isOnGround, teleported); + } + + protected void moveAbsoluteRaw(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) { setPosition(position); // Setters are intentional so it can be overridden in places like AbstractArrowEntity setYaw(yaw); 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 266189e63..d89881668 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 @@ -38,6 +38,7 @@ import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerId; import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; import org.cloudburstmc.protocol.bedrock.packet.MobArmorEquipmentPacket; import org.cloudburstmc.protocol.bedrock.packet.MobEquipmentPacket; +import org.cloudburstmc.protocol.bedrock.packet.MoveEntityDeltaPacket; import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.entity.attribute.GeyserAttributeType; @@ -69,7 +70,7 @@ import java.util.*; @Getter @Setter -public class LivingEntity extends Entity { +public class LivingEntity extends Entity implements Tickable { protected ItemData helmet = ItemData.AIR; protected ItemData chestplate = ItemData.AIR; @@ -102,6 +103,11 @@ public class LivingEntity extends Entity { @Setter(AccessLevel.NONE) private float attributeScale; + private float lerpX; + private float lerpY; + private float lerpZ; + private int lerpSteps; + public LivingEntity(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); } @@ -277,6 +283,40 @@ public class LivingEntity extends Entity { return new AttributeData(GeyserAttributeType.HEALTH.getBedrockIdentifier(), 0f, this.maxHealth, (float) Math.ceil(this.health), this.maxHealth); } + @Override + public void tick() { + lerpSteps(); + } + + protected void lerpSteps() { + if (this.lerpSteps > 0) { + float time = 1.0f / this.lerpSteps; + float lerpXTotal = lerp(time, this.position.getX(), this.lerpX); + float lerpYTotal = lerp(time, this.position.getY(), this.lerpY); + float lerpZTotal = lerp(time, this.position.getZ(), this.lerpZ); + setPosition(Vector3f.from(lerpXTotal, lerpYTotal, lerpZTotal)); + + MoveEntityDeltaPacket moveEntityPacket = new MoveEntityDeltaPacket(); + moveEntityPacket.setRuntimeEntityId(geyserId); + moveEntityPacket.setX(position.getX()); + moveEntityPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_X); + moveEntityPacket.setY(position.getY()); + moveEntityPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_Y); + moveEntityPacket.setZ(position.getZ()); + moveEntityPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_Z); + if (this.onGround) { + moveEntityPacket.getFlags().add(MoveEntityDeltaPacket.Flag.ON_GROUND); + } + session.sendUpstreamPacket(moveEntityPacket); + + this.lerpSteps--; + } + } + + private static float lerp(float time, float a, float b) { + return a + time * (b - a); + } + @Override public boolean isAlive() { return this.valid && health > 0f; @@ -304,7 +344,27 @@ public class LivingEntity extends Entity { clientVehicle.getVehicleComponent().moveRelative(relX, relY, relZ); } - super.moveRelative(relX, relY, relZ, yaw, pitch, headYaw, isOnGround); + // Logic analogous to Java Edition 1.21. + if (relX != 0 || relY != 0 || relZ != 0) { + this.lerpX = this.position.getX() + (float) relX; + this.lerpY = this.position.getY() + (float) relY; + this.lerpZ = this.position.getZ() + (float) relZ; + this.lerpSteps = 3; + } + + // Rotation lerping does not seem to be an issue with Bedrock Edition as of 1.21.22 + + super.moveRelative(0, 0, 0, yaw, pitch, headYaw, isOnGround); + } + + @Override + public void moveAbsolute(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) { + this.lerpX = position.getX(); + this.lerpY = position.getY(); + this.lerpZ = position.getZ(); + this.lerpSteps = 3; + + super.moveAbsolute(this.position, yaw, pitch, headYaw, isOnGround, teleported); } @Override diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PigEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PigEntity.java index 2ec23d673..0b8b452f9 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PigEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PigEntity.java @@ -101,6 +101,8 @@ public class PigEntity extends AnimalEntity implements Tickable, ClientVehicle { @Override public void tick() { + super.tick(); + PlayerEntity player = getPlayerPassenger(); if (player == null) { return; diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/SnifferEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/SnifferEntity.java index 11fee5bbf..916dfa224 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/SnifferEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/SnifferEntity.java @@ -101,6 +101,8 @@ public class SnifferEntity extends AnimalEntity implements Tickable { @Override public void tick() { + super.tick(); + // The java client renders digging particles on its own, but bedrock does not if (digTicks > 0 && --digTicks < DIG_START && digTicks % 5 == 0) { Vector3f rot = Vector3f.createDirectionDeg(0, -getYaw()).mul(2.25f); 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 e06af2786..43410ec35 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 @@ -148,6 +148,8 @@ public class StriderEntity extends AnimalEntity implements Tickable, ClientVehic @Override public void tick() { + super.tick(); + PlayerEntity player = getPlayerPassenger(); if (player == null) { return; diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/EnderDragonEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/EnderDragonEntity.java index 04044fcb4..23cc604d4 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/EnderDragonEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/EnderDragonEntity.java @@ -162,6 +162,8 @@ public class EnderDragonEntity extends MobEntity implements Tickable { @Override public void tick() { + super.tick(); + effectTick(); if (!getFlag(EntityFlag.NO_AI) && isAlive()) { pushSegment(); diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/ShulkerEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/ShulkerEntity.java index aecb4a915..e8810af7a 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/ShulkerEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/ShulkerEntity.java @@ -75,6 +75,18 @@ public class ShulkerEntity extends GolemEntity { } } + // As of 1.21, Java Edition does not lerp shulker steps. + + @Override + public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) { + moveRelativeRaw(relX, relY, relZ, yaw, pitch, headYaw, isOnGround); + } + + @Override + public void moveAbsolute(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) { + moveAbsoluteRaw(position, yaw, pitch, headYaw, isOnGround, teleported); + } + @Override protected boolean isEnemy() { return true; diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/WardenEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/WardenEntity.java index 2341b8c32..e860b8c65 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/WardenEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/WardenEntity.java @@ -73,6 +73,8 @@ public class WardenEntity extends MonsterEntity implements Tickable { @Override public void tick() { + super.tick(); + if (++tickCount % heartBeatDelay == 0 && !silent) { // We have to do these calculations because they're clientside on Java Edition but we mute entities // to prevent hearing their step sounds diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaSetEntityMotionTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaSetEntityMotionTranslator.java index 6bbc59e8b..715692bb1 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaSetEntityMotionTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaSetEntityMotionTranslator.java @@ -25,7 +25,6 @@ package org.geysermc.geyser.translator.protocol.java.entity; -import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.ClientboundSetEntityMotionPacket; import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.protocol.bedrock.packet.SetEntityMotionPacket; import org.geysermc.geyser.entity.type.Entity; @@ -34,6 +33,7 @@ import org.geysermc.geyser.entity.type.living.animal.horse.AbstractHorseEntity; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; +import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.ClientboundSetEntityMotionPacket; @Translator(packet = ClientboundSetEntityMotionPacket.class) public class JavaSetEntityMotionTranslator extends PacketTranslator {