Skip to content

Commit 192c755

Browse files
Fix: lerp entity position to resolve weird entity position delay. (#6059)
* Initial work on lerping entity position * Address review. * Added position check, queue packets to send immediately. * Use MoveEntityDeltaPacket instead. * TELEPORTING flag. * Use moveAbsoluteRaw where moveAbsolute has been previously used; hack around ender dragon parts * Some minor fixes * Use the new packet batch update system here as well * correct comment. * Fix overrides in ThrowableEntity * Don't lerp player movement. * Lerp old minecart movement behaviour. * Move this to AvatarEntity instead. * Minor changes. --------- Co-authored-by: onebeastchris <[email protected]>
1 parent 9ab2fd8 commit 192c755

32 files changed

+373
-97
lines changed

core/src/main/java/org/geysermc/geyser/entity/type/BoatEntity.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ protected void initializeMetadata() {
9696
}
9797

9898
@Override
99-
public void moveAbsolute(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) {
99+
public void moveAbsoluteRaw(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) {
100100
// We don't include the rotation (y) as it causes the boat to appear sideways
101101
setPosition(position.add(0d, this.definition.offset(), 0d));
102102
setYaw(yaw + 90);
@@ -122,22 +122,22 @@ public void moveAbsolute(Vector3f position, float yaw, float pitch, float headYa
122122
* Move the boat without making the adjustments needed to translate from Java
123123
*/
124124
public void moveAbsoluteWithoutAdjustments(Vector3f position, float yaw, boolean isOnGround, boolean teleported) {
125-
super.moveAbsolute(position, yaw, 0, yaw, isOnGround, teleported);
125+
super.moveAbsoluteRaw(position, yaw, 0, yaw, isOnGround, teleported);
126126
}
127127

128128
@Override
129-
public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) {
130-
super.moveRelative(relX, relY, relZ, yaw, 0, yaw, isOnGround);
129+
public void moveRelativeRaw(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) {
130+
super.moveRelativeRaw(relX, relY, relZ, yaw, 0, yaw, isOnGround);
131131
}
132132

133133
@Override
134134
public void updatePositionAndRotation(double moveX, double moveY, double moveZ, float yaw, float pitch, boolean isOnGround) {
135-
moveRelative(moveX, moveY, moveZ, yaw + 90, pitch, isOnGround);
135+
moveRelative(moveX, moveY, moveZ, yaw + 90, 0, 0, isOnGround);
136136
}
137137

138138
@Override
139139
public void updateRotation(float yaw, float pitch, boolean isOnGround) {
140-
moveRelative(0, 0, 0, yaw + 90, 0, 0, isOnGround);
140+
moveRelativeRaw(0, 0, 0, yaw + 90, 0, 0, isOnGround);
141141
}
142142

143143
public void setPaddlingLeft(BooleanEntityMetadata entityMetadata) {

core/src/main/java/org/geysermc/geyser/entity/type/DisplayBaseEntity.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public void setTranslation(EntityMetadata<Vector3f, ?> translationMeta){
6464
}
6565
if (this.vehicle == null) {
6666
this.setRiderSeatPosition(this.baseTranslation);
67-
this.moveRelative(this.baseTranslation.getX(), this.baseTranslation.getY(), this.baseTranslation.getZ(), yaw, pitch, headYaw, false);
67+
this.moveRelativeRaw(this.baseTranslation.getX(), this.baseTranslation.getY(), this.baseTranslation.getZ(), yaw, pitch, headYaw, false);
6868
} else {
6969
EntityUtils.updateMountOffset(this, this.vehicle, true, true, 0, 1);
7070
this.updateBedrockMetadata();

core/src/main/java/org/geysermc/geyser/entity/type/Entity.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -254,11 +254,11 @@ public void despawnEntity() {
254254
valid = false;
255255
}
256256

257-
public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, boolean isOnGround) {
258-
moveRelative(relX, relY, relZ, yaw, pitch, getHeadYaw(), isOnGround);
257+
public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) {
258+
moveRelativeRaw(relX, relY, relZ, yaw, pitch, headYaw, isOnGround);
259259
}
260260

261-
public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) {
261+
public void moveRelativeRaw(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) {
262262
if (this instanceof ClientVehicle clientVehicle) {
263263
if (clientVehicle.isClientControlled()) {
264264
return;
@@ -309,6 +309,10 @@ public void moveAbsolute(Vector3f position, float yaw, float pitch, boolean isOn
309309
}
310310

311311
public void moveAbsolute(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) {
312+
moveAbsoluteRaw(position, yaw, pitch, headYaw, isOnGround, teleported);
313+
}
314+
315+
public void moveAbsoluteRaw(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) {
312316
setPosition(position);
313317
// Setters are intentional so it can be overridden in places like AbstractArrowEntity
314318
setYaw(yaw);
@@ -343,7 +347,7 @@ public void teleport(Vector3f position, float yaw, float pitch, boolean isOnGrou
343347
* @param headYaw The new head rotation of the entity.
344348
*/
345349
public void updateHeadLookRotation(float headYaw) {
346-
moveRelative(0, 0, 0, getYaw(), getPitch(), headYaw, isOnGround());
350+
moveRelativeRaw(0, 0, 0, getYaw(), getPitch(), headYaw, isOnGround());
347351
}
348352

349353
/**

core/src/main/java/org/geysermc/geyser/entity/type/InteractionEntity.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -120,16 +120,16 @@ public void setDisplayName(EntityMetadata<Optional<Component>, ?> entityMetadata
120120
}
121121

122122
@Override
123-
public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) {
124-
moveAbsolute(position.add(relX, relY, relZ), yaw, pitch, headYaw, isOnGround, false);
123+
public void moveRelativeRaw(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) {
124+
moveAbsoluteRaw(position.add(relX, relY, relZ), yaw, pitch, headYaw, isOnGround, false);
125125
}
126126

127127
@Override
128-
public void moveAbsolute(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) {
128+
public void moveAbsoluteRaw(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) {
129129
if (secondEntity != null) {
130-
secondEntity.moveAbsolute(position.up(getBoundingBoxHeight()), yaw, pitch, headYaw, isOnGround, teleported);
130+
secondEntity.moveAbsoluteRaw(position.up(getBoundingBoxHeight()), yaw, pitch, headYaw, isOnGround, teleported);
131131
}
132-
super.moveAbsolute(position, yaw, pitch, headYaw, isOnGround, teleported);
132+
super.moveAbsoluteRaw(position, yaw, pitch, headYaw, isOnGround, teleported);
133133
}
134134

135135
public void setWidth(FloatEntityMetadata width) {
@@ -143,7 +143,7 @@ public void setHeight(FloatEntityMetadata height) {
143143
setBoundingBoxHeight(Math.min(height.getPrimitiveValue(), 64f));
144144

145145
if (secondEntity != null) {
146-
secondEntity.moveAbsolute(position.up(getBoundingBoxHeight()), yaw, pitch, onGround, true);
146+
secondEntity.moveAbsoluteRaw(position.up(getBoundingBoxHeight()), yaw, pitch, headYaw, onGround, true);
147147
}
148148
}
149149

core/src/main/java/org/geysermc/geyser/entity/type/LivingEntity.java

Lines changed: 110 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import lombok.Getter;
3030
import lombok.Setter;
3131
import org.checkerframework.checker.nullness.qual.Nullable;
32+
import org.cloudburstmc.math.GenericMath;
3233
import org.cloudburstmc.math.vector.Vector3f;
3334
import org.cloudburstmc.math.vector.Vector3i;
3435
import org.cloudburstmc.protocol.bedrock.data.AttributeData;
@@ -37,6 +38,7 @@
3738
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerId;
3839
import org.cloudburstmc.protocol.bedrock.packet.MobArmorEquipmentPacket;
3940
import org.cloudburstmc.protocol.bedrock.packet.MobEquipmentPacket;
41+
import org.cloudburstmc.protocol.bedrock.packet.MoveEntityDeltaPacket;
4042
import org.cloudburstmc.protocol.bedrock.packet.SetEntityDataPacket;
4143
import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket;
4244
import org.geysermc.geyser.GeyserImpl;
@@ -81,7 +83,7 @@
8183

8284
@Getter
8385
@Setter
84-
public class LivingEntity extends Entity {
86+
public class LivingEntity extends Entity implements Tickable {
8587
protected EnumMap<EquipmentSlot, GeyserItemStack> equipment = new EnumMap<>(EquipmentSlot.class);
8688

8789
@Getter(value = AccessLevel.NONE)
@@ -107,8 +109,13 @@ public class LivingEntity extends Entity {
107109
@Setter(AccessLevel.NONE)
108110
private float attributeScale;
109111

112+
private Vector3f lerpPosition;
113+
private int lerpSteps;
114+
protected boolean dirtyYaw, dirtyHeadYaw, dirtyPitch;
115+
110116
public LivingEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
111117
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
118+
this.lerpPosition = position;
112119
}
113120

114121
public GeyserItemStack getItemInSlot(EquipmentSlot slot) {
@@ -384,6 +391,108 @@ public InteractionResult interact(Hand hand) {
384391
return super.interact(hand);
385392
}
386393

394+
@Override
395+
public void setPosition(Vector3f position) {
396+
this.lerpPosition = position;
397+
super.setPosition(position);
398+
}
399+
400+
@Override
401+
public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) {
402+
if (this instanceof ClientVehicle clientVehicle) {
403+
if (clientVehicle.isClientControlled()) {
404+
return;
405+
}
406+
clientVehicle.getVehicleComponent().moveRelative(relX, relY, relZ);
407+
}
408+
409+
if (shouldLerp() && (relX != 0 || relY != 0 || relZ != 0) && position.distanceSquared(session.getPlayerEntity().position()) < 4096) {
410+
this.dirtyPitch = pitch != this.pitch;
411+
this.dirtyYaw = yaw != this.yaw;
412+
this.dirtyHeadYaw = headYaw != this.headYaw;
413+
414+
setYaw(yaw);
415+
setPitch(pitch);
416+
setHeadYaw(headYaw);
417+
418+
this.lerpPosition = Vector3f.from(lerpPosition.getX() + relX, lerpPosition.getY() + relY, lerpPosition.getZ() + relZ);
419+
this.lerpSteps = 3;
420+
} else {
421+
super.moveRelative(relX, relY, relZ, yaw, pitch, headYaw, isOnGround);
422+
}
423+
}
424+
425+
@Override
426+
public void moveAbsolute(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) {
427+
setYaw(yaw);
428+
setPitch(pitch);
429+
setHeadYaw(headYaw);
430+
431+
this.lerpPosition = position;
432+
433+
// It's vanilla behaviour to lerp if the position is within 64 blocks, however we also check if the position is close enough to the player
434+
// position to see if it can actually affect anything to save network.
435+
if (shouldLerp() && position.distanceSquared(this.position) < 4096 && position.distanceSquared(session.getPlayerEntity().position()) < 4096) {
436+
this.dirtyPitch = this.dirtyYaw = this.dirtyHeadYaw = true;
437+
this.lerpSteps = 3;
438+
} else {
439+
super.moveAbsolute(position, yaw, pitch, headYaw, isOnGround, teleported);
440+
}
441+
}
442+
443+
public boolean shouldLerp() {
444+
return true;
445+
}
446+
447+
@Override
448+
public void tick() {
449+
if (this.lerpSteps > 0) {
450+
float time = 1.0f / this.lerpSteps;
451+
float lerpXTotal = GenericMath.lerp(this.position.getX(), this.lerpPosition.getX(), time);
452+
float lerpYTotal = GenericMath.lerp(this.position.getY(), this.lerpPosition.getY(), time);
453+
float lerpZTotal = GenericMath.lerp(this.position.getZ(), this.lerpPosition.getZ(), time);
454+
455+
MoveEntityDeltaPacket moveEntityPacket = new MoveEntityDeltaPacket();
456+
moveEntityPacket.setRuntimeEntityId(geyserId);
457+
moveEntityPacket.setX(lerpXTotal);
458+
moveEntityPacket.setY(lerpYTotal);
459+
moveEntityPacket.setZ(lerpZTotal);
460+
moveEntityPacket.setYaw(this.yaw);
461+
moveEntityPacket.setPitch(this.pitch);
462+
moveEntityPacket.setHeadYaw(this.headYaw);
463+
if (onGround) {
464+
moveEntityPacket.getFlags().add(MoveEntityDeltaPacket.Flag.ON_GROUND);
465+
}
466+
if (lerpXTotal != this.position.getX()) {
467+
moveEntityPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_X);
468+
}
469+
if (lerpYTotal != this.position.getY()) {
470+
moveEntityPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_Y);
471+
}
472+
if (lerpZTotal != this.position.getZ()) {
473+
moveEntityPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_Z);
474+
}
475+
if (this.dirtyYaw) {
476+
moveEntityPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_YAW);
477+
}
478+
if (this.dirtyHeadYaw) {
479+
moveEntityPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_HEAD_YAW);
480+
}
481+
if (this.dirtyPitch) {
482+
moveEntityPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_PITCH);
483+
}
484+
moveEntityPacket.getFlags().add(MoveEntityDeltaPacket.Flag.TELEPORTING);
485+
486+
this.dirtyPitch = this.dirtyYaw = this.dirtyHeadYaw = false;
487+
488+
// Queue this and send it immediately later with the rest.
489+
session.getQueuedImmediatelyPackets().add(moveEntityPacket);
490+
491+
this.position = Vector3f.from(lerpXTotal, lerpYTotal, lerpZTotal);
492+
this.lerpSteps--;
493+
}
494+
}
495+
387496
@Override
388497
public boolean setBoundingBoxHeight(float height) {
389498
if (valid && this instanceof ClientVehicle clientVehicle) {

0 commit comments

Comments
 (0)