From 754bb42c1976c32c4d1fed06fbdaf30fd67b226b Mon Sep 17 00:00:00 2001 From: David Choo Date: Sat, 18 Dec 2021 11:43:57 -0500 Subject: [PATCH] Fix some mount offsets and strange behaviors with GSit (#2701) * Prevent boats from floating when a Bedrock player rides in the back * Update llama and shulker mount height offset * Fix Trader llama mount offset * Change passengers to an IntList Also move rotation lock stuff to EntityUtils * Allow EntityCache.getEntityByJavaId to return the session's player Removes many checks from several translators. * Fix mount offset on armorstands and area effect clouds Also prevent area effect clouds from despawning when used as an invisible entity (used in GSit to offset players riding on other players) * Update mount offsets on height changes TODO test this * Actually update vehicleId and optimize StriderEntity metadata update * Don't hide marker armor stands and properly update mount offsets * What? * Remove y offset for Shulkers riding other entities * Confirm teleports in the order received This allows Bedrock players to move after standing up when using GSit * Fix mount offset for riders on baby zoglins * Cache only the latest teleport and confirm teleports immediately * Fix crawling with GSit * Inline var and undo respawn method * Use Entity reference for vehicles * Remove ridingVehicleEntity from GeyserSession * Use Entity references for passengers and drop cachedPlayerEntityLinks * Reuse variable and remove setVehicle(null) --- .../entity/type/AreaEffectCloudEntity.java | 9 +- .../geyser/entity/type/BoatEntity.java | 12 ++- .../geysermc/geyser/entity/type/Entity.java | 43 ++++++-- .../geyser/entity/type/FishingHookEntity.java | 8 +- .../entity/type/living/ArmorStandEntity.java | 9 +- .../type/living/animal/StriderEntity.java | 15 +-- .../type/living/monster/GuardianEntity.java | 8 +- .../type/living/monster/PiglinEntity.java | 2 + .../type/living/monster/WitherEntity.java | 10 +- .../type/living/monster/ZoglinEntity.java | 14 ++- .../type/living/monster/ZombieEntity.java | 2 + .../entity/type/player/PlayerEntity.java | 8 -- .../type/player/SessionPlayerEntity.java | 10 +- .../geyser/session/GeyserSession.java | 81 +++------------ .../geyser/session/cache/EntityCache.java | 16 +-- .../geyser/session/cache/TeleportCache.java | 4 + .../geyser/session/cache/WorldBorder.java | 2 +- .../BedrockMoveEntityAbsoluteTranslator.java | 2 +- .../bedrock/BedrockPlayerInputTranslator.java | 2 +- .../player/BedrockActionTranslator.java | 31 +++--- .../player/BedrockInteractTranslator.java | 3 +- .../player/BedrockMovePlayerTranslator.java | 3 +- .../player/BedrockRiderJumpTranslator.java | 2 +- .../java/entity/JavaAnimateTranslator.java | 7 +- .../entity/JavaEntityEventTranslator.java | 7 +- .../JavaMoveEntityPosRotTranslator.java | 3 - .../entity/JavaMoveEntityPosTranslator.java | 7 +- .../entity/JavaMoveEntityRotTranslator.java | 3 - .../entity/JavaMoveVehicleTranslator.java | 2 +- .../entity/JavaRemoveMobEffectTranslator.java | 7 +- .../java/entity/JavaRotateHeadTranslator.java | 8 +- .../entity/JavaSetEntityDataTranslator.java | 7 +- .../entity/JavaSetEntityLinkTranslator.java | 36 +++---- .../entity/JavaSetEntityMotionTranslator.java | 9 +- .../entity/JavaSetEquipmentTranslator.java | 8 +- .../entity/JavaSetPassengersTranslator.java | 99 +++++++------------ .../entity/JavaTakeItemEntityTranslator.java | 7 +- .../entity/JavaTeleportEntityTranslator.java | 3 - .../JavaUpdateAttributesTranslator.java | 7 +- .../entity/JavaUpdateMobEffectTranslator.java | 7 +- .../player/JavaPlayerPositionTranslator.java | 56 ++++++----- .../entity/spawn/JavaAddEntityTranslator.java | 7 +- .../org/geysermc/geyser/util/EntityUtils.java | 43 +++++--- 43 files changed, 263 insertions(+), 366 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/AreaEffectCloudEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/AreaEffectCloudEntity.java index 6063c81f9..3b6b4448c 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/AreaEffectCloudEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/AreaEffectCloudEntity.java @@ -47,18 +47,19 @@ public class AreaEffectCloudEntity extends Entity { protected void initializeMetadata() { super.initializeMetadata(); // Without this the cloud doesn't appear, - dirtyMetadata.put(EntityData.AREA_EFFECT_CLOUD_DURATION, 600); + dirtyMetadata.put(EntityData.AREA_EFFECT_CLOUD_DURATION, Integer.MAX_VALUE); // This disabled client side shrink of the cloud dirtyMetadata.put(EntityData.AREA_EFFECT_CLOUD_RADIUS, 0.0f); - dirtyMetadata.put(EntityData.AREA_EFFECT_CLOUD_CHANGE_RATE, -0.005f); - dirtyMetadata.put(EntityData.AREA_EFFECT_CLOUD_CHANGE_ON_PICKUP, -0.5f); + dirtyMetadata.put(EntityData.AREA_EFFECT_CLOUD_CHANGE_RATE, Float.MIN_VALUE); + dirtyMetadata.put(EntityData.AREA_EFFECT_CLOUD_CHANGE_ON_PICKUP, Float.MIN_VALUE); setFlag(EntityFlag.FIRE_IMMUNE, true); } public void setRadius(FloatEntityMetadata entityMetadata) { - float value = entityMetadata.getPrimitiveValue(); + // Anything less than 0.5 will cause the cloud to despawn + float value = Math.max(entityMetadata.getPrimitiveValue(), 0.5f); dirtyMetadata.put(EntityData.AREA_EFFECT_CLOUD_RADIUS, value); dirtyMetadata.put(EntityData.BOUNDING_BOX_WIDTH, 2.0f * value); } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/BoatEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/BoatEntity.java index ac1b3fcbd..8d6b793a9 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/BoatEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/BoatEntity.java @@ -84,8 +84,12 @@ public class BoatEntity extends Entity { MoveEntityAbsolutePacket moveEntityPacket = new MoveEntityAbsolutePacket(); moveEntityPacket.setRuntimeEntityId(geyserId); - // Minimal glitching when ClientboundMoveVehiclePacket is sent - moveEntityPacket.setPosition(session.getRidingVehicleEntity() == this ? position.up(EntityDefinitions.PLAYER.offset() - this.definition.offset()) : this.position); + if (session.getPlayerEntity().getVehicle() == this && session.getPlayerEntity().isRidingInFront()) { + // Minimal glitching when ClientboundMoveVehiclePacket is sent + moveEntityPacket.setPosition(position.up(EntityDefinitions.PLAYER.offset() - this.definition.offset())); + } else { + moveEntityPacket.setPosition(this.position); + } moveEntityPacket.setRotation(getBedrockRotation()); moveEntityPacket.setOnGround(isOnGround); moveEntityPacket.setTeleported(teleported); @@ -128,7 +132,7 @@ public class BoatEntity extends Entity { paddleTimeLeft = 0f; if (!this.passengers.isEmpty()) { // Get the entity by the first stored passenger and convey motion in this manner - Entity entity = session.getEntityCache().getEntityByJavaId(this.passengers.iterator().nextLong()); + Entity entity = this.passengers.get(0); if (entity != null) { updateLeftPaddle(session, entity); } @@ -144,7 +148,7 @@ public class BoatEntity extends Entity { if (isPaddlingRight) { paddleTimeRight = 0f; if (!this.passengers.isEmpty()) { - Entity entity = session.getEntityCache().getEntityByJavaId(this.passengers.iterator().nextLong()); + Entity entity = this.passengers.get(0); if (entity != null) { updateRightPaddle(session, entity); } 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 479ec2e8c..5074751bf 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 @@ -39,7 +39,6 @@ import com.nukkitx.protocol.bedrock.packet.AddEntityPacket; import com.nukkitx.protocol.bedrock.packet.MoveEntityAbsolutePacket; import com.nukkitx.protocol.bedrock.packet.RemoveEntityPacket; import com.nukkitx.protocol.bedrock.packet.SetEntityDataPacket; -import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import lombok.AccessLevel; import lombok.Getter; import lombok.Setter; @@ -48,8 +47,11 @@ import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.entity.GeyserDirtyMetadata; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.text.MessageTranslator; +import org.geysermc.geyser.util.EntityUtils; import org.geysermc.geyser.util.MathUtils; +import java.util.Collections; +import java.util.List; import java.util.Optional; import java.util.UUID; @@ -90,7 +92,8 @@ public class Entity { protected String nametag = ""; /* Metadata end */ - protected LongOpenHashSet passengers = new LongOpenHashSet(); + protected List passengers = Collections.emptyList(); + protected Entity vehicle; /** * A container to store temporary metadata before it's sent to Bedrock. */ @@ -181,11 +184,11 @@ public class Entity { public boolean despawnEntity() { if (!valid) return true; - for (long passenger : passengers) { // Make sure all passengers on the despawned entity are updated - Entity entity = session.getEntityCache().getEntityByJavaId(passenger); - if (entity == null) continue; - entity.setFlag(EntityFlag.RIDING, false); - entity.updateBedrockMetadata(); + for (Entity passenger : passengers) { // Make sure all passengers on the despawned entity are updated + if (passenger == null) continue; + passenger.setVehicle(null); + passenger.setFlag(EntityFlag.RIDING, false); + passenger.updateBedrockMetadata(); } RemoveEntityPacket removeEntityPacket = new RemoveEntityPacket(); @@ -391,6 +394,8 @@ public class Entity { if (height != boundingBoxHeight) { boundingBoxHeight = height; dirtyMetadata.put(EntityData.BOUNDING_BOX_HEIGHT, boundingBoxHeight); + + updatePassengerOffsets(); } } @@ -435,6 +440,30 @@ public class Entity { return Vector3f.from(pitch, headYaw, yaw); } + /** + * Update the mount offsets of each passenger on this vehicle + */ + protected void updatePassengerOffsets() { + for (Entity passenger : passengers) { + if (passenger != null) { + boolean rider = passengers.get(0) == this; + EntityUtils.updateMountOffset(passenger, this, rider, true, passengers.size() > 1); + passenger.updateBedrockMetadata(); + } + } + } + + /** + * Update this entity's mount offset + */ + protected void updateMountOffset() { + if (vehicle != null) { + boolean rider = vehicle.getPassengers().get(0) == this; + EntityUtils.updateMountOffset(this, vehicle, rider, true, vehicle.getPassengers().size() > 1); + updateBedrockMetadata(); + } + } + @SuppressWarnings("unchecked") public I as(Class entityClass) { return entityClass.isInstance(this) ? (I) this : null; diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/FishingHookEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/FishingHookEntity.java index b5774bd78..9a85a14a2 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/FishingHookEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/FishingHookEntity.java @@ -78,13 +78,7 @@ public class FishingHookEntity extends ThrowableEntity { public void setHookedEntity(IntEntityMetadata entityMetadata) { int hookedEntityId = entityMetadata.getPrimitiveValue() - 1; - Entity entity; - if (session.getPlayerEntity().getEntityId() == hookedEntityId) { - entity = session.getPlayerEntity(); - } else { - entity = session.getEntityCache().getEntityByJavaId(hookedEntityId); - } - + Entity entity = session.getEntityCache().getEntityByJavaId(hookedEntityId); if (entity != null) { bedrockTargetId = entity.getGeyserId(); dirtyMetadata.put(EntityData.TARGET_EID, bedrockTargetId); diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/ArmorStandEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/ArmorStandEntity.java index fb459bf54..ea2452251 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/ArmorStandEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/ArmorStandEntity.java @@ -146,13 +146,14 @@ public class ArmorStandEntity extends LivingEntity { isMarker = (xd & 0x10) == 0x10; if (oldIsMarker != isMarker) { if (isMarker) { - dirtyMetadata.put(EntityData.BOUNDING_BOX_WIDTH, 0.0f); - dirtyMetadata.put(EntityData.BOUNDING_BOX_HEIGHT, 0.0f); + setBoundingBoxWidth(0.0f); + setBoundingBoxHeight(0.0f); dirtyMetadata.put(EntityData.SCALE, 0f); } else { toggleSmallStatus(); } + updateMountOffset(); updateSecondEntityStatus(false); } @@ -376,8 +377,8 @@ public class ArmorStandEntity extends LivingEntity { * If this armor stand is not a marker, set its bounding box size and scale. */ private void toggleSmallStatus() { - dirtyMetadata.put(EntityData.BOUNDING_BOX_WIDTH, isSmall ? 0.25f : definition.width()); - dirtyMetadata.put(EntityData.BOUNDING_BOX_HEIGHT, isSmall ? 0.9875f : definition.height()); + setBoundingBoxWidth(isSmall ? 0.25f : definition.width()); + setBoundingBoxHeight(isSmall ? 0.9875f : definition.height()); dirtyMetadata.put(EntityData.SCALE, isSmall ? 0.55f : 1f); } 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 d684fba06..30f21a526 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 @@ -60,12 +60,8 @@ public class StriderEntity extends AnimalEntity { // Needs to copy the parent state if (getFlag(EntityFlag.RIDING)) { boolean parentShaking = false; - //TODO optimize - for (Entity ent : session.getEntityCache().getEntities().values()) { - if (ent.getPassengers().contains(entityId) && ent instanceof StriderEntity) { - parentShaking = ent.getFlag(EntityFlag.SHAKING); - break; - } + if (vehicle instanceof StriderEntity) { + parentShaking = vehicle.getFlag(EntityFlag.SHAKING); } setFlag(EntityFlag.BREATHING, !parentShaking); @@ -76,10 +72,9 @@ public class StriderEntity extends AnimalEntity { } // Update the passengers if we have any - for (long passenger : passengers) { - Entity passengerEntity = session.getEntityCache().getEntityByJavaId(passenger); - if (passengerEntity != null) { - passengerEntity.updateBedrockMetadata(); + for (Entity passenger : passengers) { + if (passenger != null) { + passenger.updateBedrockMetadata(); } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/GuardianEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/GuardianEntity.java index 0190f3c60..1def7a4d6 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/GuardianEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/GuardianEntity.java @@ -42,13 +42,7 @@ public class GuardianEntity extends MonsterEntity { public void setGuardianTarget(IntEntityMetadata entityMetadata) { int entityId = entityMetadata.getPrimitiveValue(); - Entity entity; - if (session.getPlayerEntity().getEntityId() == entityId) { - entity = session.getPlayerEntity(); - } else { - entity = session.getEntityCache().getEntityByJavaId(entityId); - } - + Entity entity = session.getEntityCache().getEntityByJavaId(entityId); if (entity != null) { dirtyMetadata.put(EntityData.TARGET_EID, entity.getGeyserId()); } else { diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/PiglinEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/PiglinEntity.java index b98d6eabc..c68786703 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/PiglinEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/PiglinEntity.java @@ -44,6 +44,8 @@ public class PiglinEntity extends BasePiglinEntity { boolean isBaby = entityMetadata.getPrimitiveValue(); dirtyMetadata.put(EntityData.SCALE, isBaby? .55f : 1f); setFlag(EntityFlag.BABY, isBaby); + + updateMountOffset(); } public void setChargingCrossbow(BooleanEntityMetadata entityMetadata) { diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/WitherEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/WitherEntity.java index 17da0a611..81f183e2b 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/WitherEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/WitherEntity.java @@ -60,15 +60,11 @@ public class WitherEntity extends MonsterEntity { private void setTargetId(EntityData entityData, IntEntityMetadata entityMetadata) { int entityId = entityMetadata.getPrimitiveValue(); - Entity entity; - if (session.getPlayerEntity().getEntityId() == entityId) { - entity = session.getPlayerEntity(); - } else { - entity = session.getEntityCache().getEntityByJavaId(entityId); - } - + Entity entity = session.getEntityCache().getEntityByJavaId(entityId); if (entity != null) { dirtyMetadata.put(entityData, entity.getGeyserId()); + } else { + dirtyMetadata.put(entityData, (long) 0); } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/ZoglinEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/ZoglinEntity.java index f0fe101da..d69739abd 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/ZoglinEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/ZoglinEntity.java @@ -42,7 +42,17 @@ public class ZoglinEntity extends MonsterEntity { public void setBaby(BooleanEntityMetadata entityMetadata) { boolean isBaby = entityMetadata.getPrimitiveValue(); - dirtyMetadata.put(EntityData.SCALE, isBaby? .55f : 1f); - setFlag(EntityFlag.BABY, isBaby); + if (isBaby != getFlag(EntityFlag.BABY)) { + dirtyMetadata.put(EntityData.SCALE, isBaby ? .55f : 1f); + setFlag(EntityFlag.BABY, isBaby); + + updatePassengerOffsets(); + } + } + + @Override + public float getBoundingBoxHeight() { + float scale = getFlag(EntityFlag.BABY) ? 0.55f : 1f; + return scale * definition.height(); } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/ZombieEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/ZombieEntity.java index 9e3301b48..d2cc16601 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/ZombieEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/ZombieEntity.java @@ -45,6 +45,8 @@ public class ZombieEntity extends MonsterEntity { boolean isBaby = entityMetadata.getPrimitiveValue(); dirtyMetadata.put(EntityData.SCALE, isBaby ? .55f : 1.0f); setFlag(EntityFlag.BABY, isBaby); + + updateMountOffset(); } public void setConvertingToDrowned(BooleanEntityMetadata entityMetadata) { diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java index 1d59b83db..cfd39e511 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java @@ -123,14 +123,6 @@ public class PlayerEntity extends LivingEntity { setFlagsDirty(false); - long linkedEntityId = session.getEntityCache().getCachedPlayerEntityLink(entityId); - if (linkedEntityId != -1) { - Entity linkedEntity = session.getEntityCache().getEntityByJavaId(linkedEntityId); - if (linkedEntity != null) { - addPlayerPacket.getEntityLinks().add(new EntityLinkData(linkedEntity.getGeyserId(), geyserId, EntityLinkData.Type.RIDER, false, false)); - } - } - valid = true; session.sendUpstreamPacket(addPlayerPacket); } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java index dff9fbfa2..47a254fa8 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java @@ -35,11 +35,14 @@ import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.AttributeData; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; +import com.nukkitx.protocol.bedrock.packet.MovePlayerPacket; +import com.nukkitx.protocol.bedrock.packet.RespawnPacket; import com.nukkitx.protocol.bedrock.packet.UpdateAttributesPacket; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import lombok.Getter; import org.geysermc.geyser.entity.attribute.GeyserAttributeType; import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.util.AttributeUtils; import java.util.Collections; @@ -73,7 +76,7 @@ public class SessionPlayerEntity extends PlayerEntity { private final GeyserSession session; public SessionPlayerEntity(GeyserSession session) { - super(session, 1, 1, new GameProfile(UUID.randomUUID(), "unknown"), Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0); + super(session, -1, 1, new GameProfile(UUID.randomUUID(), "unknown"), Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0); valid = true; this.session = session; @@ -112,7 +115,10 @@ public class SessionPlayerEntity extends PlayerEntity { @Override public void setFlags(ByteEntityMetadata entityMetadata) { super.setFlags(entityMetadata); - session.setSwimmingInWater((entityMetadata.getPrimitiveValue() & 0x10) == 0x10 && getFlag(EntityFlag.SPRINTING)); + // Swimming/crawling is controlled by the Java server + boolean swimming = (entityMetadata.getPrimitiveValue() & 0x10) == 0x10; + session.setSwimming(swimming); + session.setSwimmingInWater(swimming && getFlag(EntityFlag.SPRINTING)); refreshSpeed = true; } diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index 2e3368356..c67299466 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -42,9 +42,7 @@ import com.github.steveice10.mc.protocol.data.game.recipe.Recipe; import com.github.steveice10.mc.protocol.data.game.statistic.CustomStatistic; import com.github.steveice10.mc.protocol.data.game.statistic.Statistic; import com.github.steveice10.mc.protocol.packet.handshake.serverbound.ClientIntentionPacket; -import com.github.steveice10.mc.protocol.packet.ingame.serverbound.level.ServerboundAcceptTeleportationPacket; import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerPosPacket; -import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerPosRotPacket; import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundPlayerAbilitiesPacket; import com.github.steveice10.mc.protocol.packet.login.serverbound.ServerboundCustomQueryPacket; import com.github.steveice10.packetlib.BuiltinFlags; @@ -68,7 +66,6 @@ import it.unimi.dsi.fastutil.ints.*; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import it.unimi.dsi.fastutil.objects.ObjectIterator; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import lombok.AccessLevel; import lombok.Getter; @@ -167,7 +164,8 @@ public class GeyserSession implements GeyserConnection, CommandSender { private final TagCache tagCache; private final WorldCache worldCache; - private final Int2ObjectMap teleportMap = new Int2ObjectOpenHashMap<>(); + @Setter + private TeleportCache unconfirmedTeleport; private final WorldBorder worldBorder; /** @@ -333,9 +331,6 @@ public class GeyserSession implements GeyserConnection, CommandSender { @Setter private Vector3f lastInteractionPlayerPosition = Vector3f.ZERO; - @Setter - private Entity ridingVehicleEntity; - /** * The entity that the client is currently looking at. */ @@ -1247,73 +1242,23 @@ public class GeyserSession implements GeyserConnection, CommandSender { return itemNetId.getAndIncrement(); } - public void addTeleport(TeleportCache teleportCache) { - teleportMap.put(teleportCache.getTeleportConfirmId(), teleportCache); - - ObjectIterator> it = teleportMap.int2ObjectEntrySet().iterator(); - - // Remove any teleports with a higher number - maybe this is a world change that reset the ID to 0? - while (it.hasNext()) { - Int2ObjectMap.Entry entry = it.next(); - int nextID = entry.getValue().getTeleportConfirmId(); - if (nextID > teleportCache.getTeleportConfirmId()) { - it.remove(); - } - } - } - public void confirmTeleport(Vector3d position) { - if (teleportMap.size() == 0) { + if (unconfirmedTeleport == null) { return; } - int teleportID = -1; - for (Int2ObjectMap.Entry entry : teleportMap.int2ObjectEntrySet()) { - if (entry.getValue().canConfirm(position)) { - if (entry.getValue().getTeleportConfirmId() > teleportID) { - teleportID = entry.getValue().getTeleportConfirmId(); - } - } + if (unconfirmedTeleport.canConfirm(position)) { + unconfirmedTeleport = null; + return; } - if (teleportID != -1) { - ObjectIterator> it = teleportMap.int2ObjectEntrySet().iterator(); - - // Confirm the current teleport and any earlier ones - while (it.hasNext()) { - TeleportCache entry = it.next().getValue(); - int nextID = entry.getTeleportConfirmId(); - if (nextID <= teleportID) { - ServerboundAcceptTeleportationPacket teleportConfirmPacket = new ServerboundAcceptTeleportationPacket(nextID); - sendDownstreamPacket(teleportConfirmPacket); - // Servers (especially ones like Hypixel) expect exact coordinates given back to them. - ServerboundMovePlayerPosRotPacket positionPacket = new ServerboundMovePlayerPosRotPacket(playerEntity.isOnGround(), - entry.getX(), entry.getY(), entry.getZ(), entry.getYaw(), entry.getPitch()); - sendDownstreamPacket(positionPacket); - it.remove(); - geyser.getLogger().debug("Confirmed teleport " + nextID); - } - } - } - - if (teleportMap.size() > 0) { - int resendID = -1; - for (Int2ObjectMap.Entry entry : teleportMap.int2ObjectEntrySet()) { - TeleportCache teleport = entry.getValue(); - teleport.incrementUnconfirmedFor(); - if (teleport.shouldResend()) { - if (teleport.getTeleportConfirmId() >= resendID) { - resendID = teleport.getTeleportConfirmId(); - } - } - } - - if (resendID != -1) { - geyser.getLogger().debug("Resending teleport " + resendID); - TeleportCache teleport = teleportMap.get(resendID); - getPlayerEntity().moveAbsolute(Vector3f.from(teleport.getX(), teleport.getY(), teleport.getZ()), - teleport.getYaw(), teleport.getPitch(), playerEntity.isOnGround(), true); - } + // Resend the teleport every few packets until Bedrock responds + unconfirmedTeleport.incrementUnconfirmedFor(); + if (unconfirmedTeleport.shouldResend()) { + unconfirmedTeleport.resetUnconfirmedFor(); + geyser.getLogger().debug("Resending teleport " + unconfirmedTeleport.getTeleportConfirmId()); + getPlayerEntity().moveAbsolute(Vector3f.from(unconfirmedTeleport.getX(), unconfirmedTeleport.getY(), unconfirmedTeleport.getZ()), + unconfirmedTeleport.getYaw(), unconfirmedTeleport.getPitch(), playerEntity.isOnGround(), true); } } diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/EntityCache.java b/core/src/main/java/org/geysermc/geyser/session/cache/EntityCache.java index 5d99ba0e3..f6184c112 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/EntityCache.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/EntityCache.java @@ -56,15 +56,12 @@ public class EntityCache { private final Long2LongMap entityIdTranslations = new Long2LongOpenHashMap(); private final Map playerEntities = new Object2ObjectOpenHashMap<>(); private final Map bossBars = new Object2ObjectOpenHashMap<>(); - private final Long2LongMap cachedPlayerEntityLinks = new Long2LongOpenHashMap(); @Getter private final AtomicLong nextEntityId = new AtomicLong(2L); public EntityCache(GeyserSession session) { this.session = session; - - cachedPlayerEntityLinks.defaultReturnValue(-1L); } public void spawnEntity(Entity entity) { @@ -112,8 +109,6 @@ public class EntityCache { } session.getPlayerWithCustomHeads().clear(); - // As a precaution - cachedPlayerEntityLinks.clear(); } public Entity getEntityByGeyserId(long geyserId) { @@ -121,6 +116,9 @@ public class EntityCache { } public Entity getEntityByJavaId(long javaId) { + if (javaId == session.getPlayerEntity().getEntityId()) { + return session.getPlayerEntity(); + } return entities.get(entityIdTranslations.get(javaId)); } @@ -160,14 +158,6 @@ public class EntityCache { bossBars.values().forEach(BossBar::updateBossBar); } - public long getCachedPlayerEntityLink(long playerId) { - return cachedPlayerEntityLinks.remove(playerId); - } - - public void addCachedPlayerEntityLink(long playerId, long linkedEntityId) { - cachedPlayerEntityLinks.put(playerId, linkedEntityId); - } - public List getTickableEntities() { return tickableEntities; } diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/TeleportCache.java b/core/src/main/java/org/geysermc/geyser/session/cache/TeleportCache.java index cc9a7c09f..5eff56a97 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/TeleportCache.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/TeleportCache.java @@ -66,6 +66,10 @@ public class TeleportCache { unconfirmedFor++; } + public void resetUnconfirmedFor() { + unconfirmedFor = 0; + } + public boolean shouldResend() { return unconfirmedFor >= RESEND_THRESHOLD; } diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/WorldBorder.java b/core/src/main/java/org/geysermc/geyser/session/cache/WorldBorder.java index 01c5949c7..e4ba674ef 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/WorldBorder.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/WorldBorder.java @@ -151,7 +151,7 @@ public class WorldBorder { // Move the player back, but allow gravity to take place // Teleported = true makes going back better, but disconnects the player from their mounted entity playerEntity.moveAbsolute(Vector3f.from(playerEntity.getPosition().getX(), (newPosition.getY() - EntityDefinitions.PLAYER.offset()), playerEntity.getPosition().getZ()), - playerEntity.getYaw(), playerEntity.getPitch(), playerEntity.getHeadYaw(), playerEntity.isOnGround(), session.getRidingVehicleEntity() == null); + playerEntity.getYaw(), playerEntity.getPitch(), playerEntity.getHeadYaw(), playerEntity.isOnGround(), playerEntity.getVehicle() == null); } return isInWorldBorder; } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockMoveEntityAbsoluteTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockMoveEntityAbsoluteTranslator.java index 6d971daeb..3f0e253c6 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockMoveEntityAbsoluteTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockMoveEntityAbsoluteTranslator.java @@ -45,7 +45,7 @@ public class BedrockMoveEntityAbsoluteTranslator extends PacketTranslator case LEAVE_VEHICLE: ServerboundPlayerCommandPacket sneakPacket = new ServerboundPlayerCommandPacket((int) entity.getEntityId(), PlayerState.START_SNEAKING); session.sendDownstreamPacket(sneakPacket); - session.setRidingVehicleEntity(null); break; case MOUSEOVER: // Handle the buttons for mobile - "Mount", etc; and the suggestions for console - "ZL: Mount", etc @@ -97,7 +96,7 @@ public class BedrockInteractTranslator extends PacketTranslator break; case OPEN_INVENTORY: if (session.getOpenInventory() == null) { - Entity ridingEntity = session.getRidingVehicleEntity(); + Entity ridingEntity = session.getPlayerEntity().getVehicle(); if (ridingEntity instanceof AbstractHorseEntity) { if (ridingEntity.getFlag(EntityFlag.TAMED)) { // We should request to open the horse inventory instead diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockMovePlayerTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockMovePlayerTranslator.java index 1555722d6..7c170fdbc 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockMovePlayerTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockMovePlayerTranslator.java @@ -67,7 +67,8 @@ public class BedrockMovePlayerTranslator extends PacketTranslator { @Override public void translate(GeyserSession session, RiderJumpPacket packet) { - Entity vehicle = session.getRidingVehicleEntity(); + Entity vehicle = session.getPlayerEntity().getVehicle(); if (vehicle instanceof AbstractHorseEntity) { ServerboundPlayerCommandPacket playerCommandPacket = new ServerboundPlayerCommandPacket((int) vehicle.getEntityId(), PlayerState.START_HORSE_JUMP, packet.getJumpStrength()); session.sendDownstreamPacket(playerCommandPacket); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaAnimateTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaAnimateTranslator.java index d7fc79f1e..fb4e2653d 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaAnimateTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaAnimateTranslator.java @@ -41,12 +41,7 @@ public class JavaAnimateTranslator extends PacketTranslator definition = entity.getDefinition(); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaSetEntityLinkTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaSetEntityLinkTranslator.java index 88f1c8ae1..deed9e65e 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaSetEntityLinkTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaSetEntityLinkTranslator.java @@ -43,33 +43,23 @@ public class JavaSetEntityLinkTranslator extends PacketTranslator { @Override public void translate(GeyserSession session, ClientboundSetPassengersPacket packet) { - Entity entity; - if (packet.getEntityId() == session.getPlayerEntity().getEntityId()) { - entity = session.getPlayerEntity(); - } else { - entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId()); - } - + Entity entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId()); if (entity == null) return; - LongOpenHashSet passengers = entity.getPassengers().clone(); - boolean rider = true; + // Handle new/existing passengers + List newPassengers = new ArrayList<>(); for (long passengerId : packet.getPassengerIds()) { Entity passenger = session.getEntityCache().getEntityByJavaId(passengerId); - if (passengerId == session.getPlayerEntity().getEntityId()) { - passenger = session.getPlayerEntity(); - session.setRidingVehicleEntity(entity); + if (passenger == session.getPlayerEntity()) { + session.getPlayerEntity().setVehicle(entity); // We need to confirm teleports before entering a vehicle, or else we will likely exit right out session.confirmTeleport(passenger.getPosition().sub(0, EntityDefinitions.PLAYER.offset(), 0).toDouble()); } - // Passenger hasn't loaded in (likely since we're waiting for a skin response) - // and entity link needs to be set later - if (passenger == null && passengerId != 0) { - session.getEntityCache().addCachedPlayerEntityLink(passengerId, packet.getEntityId()); - } if (passenger == null) { + // Can occur if the passenger is outside the client's tracking range + // In this case, another SetPassengers packet will be sent when the passenger is spawned. continue; } + boolean rider = packet.getPassengerIds()[0] == passengerId; EntityLinkData.Type type = rider ? EntityLinkData.Type.RIDER : EntityLinkData.Type.PASSENGER; SetEntityLinkPacket linkPacket = new SetEntityLinkPacket(); - linkPacket.setEntityLink(new EntityLinkData(entity.getGeyserId(), passenger.getGeyserId(), type, false)); + linkPacket.setEntityLink(new EntityLinkData(entity.getGeyserId(), passenger.getGeyserId(), type, false, false)); session.sendUpstreamPacket(linkPacket); - passengers.add(passengerId); - - // Head rotation on boats - if (entity.getDefinition() == EntityDefinitions.BOAT) { - passenger.getDirtyMetadata().put(EntityData.RIDER_ROTATION_LOCKED, (byte) 1); - passenger.getDirtyMetadata().put(EntityData.RIDER_MAX_ROTATION, 90f); - passenger.getDirtyMetadata().put(EntityData.RIDER_MIN_ROTATION, 1f); - passenger.getDirtyMetadata().put(EntityData.RIDER_ROTATION_OFFSET, -90f); - } else { - passenger.getDirtyMetadata().put(EntityData.RIDER_ROTATION_LOCKED, (byte) 0); - passenger.getDirtyMetadata().put(EntityData.RIDER_MAX_ROTATION, 0f); - passenger.getDirtyMetadata().put(EntityData.RIDER_MIN_ROTATION, 0f); - } - - passenger.updateBedrockMetadata(); - rider = false; - } - - entity.setPassengers(passengers); - - for (long passengerId : entity.getPassengers()) { - Entity passenger = session.getEntityCache().getEntityByJavaId(passengerId); - if (passengerId == session.getPlayerEntity().getEntityId()) { - passenger = session.getPlayerEntity(); - } - if (passenger == null) { - continue; - } - if (Arrays.stream(packet.getPassengerIds()).noneMatch(id -> id == passengerId)) { - SetEntityLinkPacket linkPacket = new SetEntityLinkPacket(); - linkPacket.setEntityLink(new EntityLinkData(entity.getGeyserId(), passenger.getGeyserId(), EntityLinkData.Type.REMOVE, false)); - session.sendUpstreamPacket(linkPacket); - passengers.remove(passenger.getEntityId()); - passenger.getDirtyMetadata().put(EntityData.RIDER_ROTATION_LOCKED, (byte) 0); - passenger.getDirtyMetadata().put(EntityData.RIDER_MAX_ROTATION, 0f); - passenger.getDirtyMetadata().put(EntityData.RIDER_MIN_ROTATION, 0f); - passenger.getDirtyMetadata().put(EntityData.RIDER_ROTATION_OFFSET, 0f); - - EntityUtils.updateMountOffset(passenger, entity, false, false, (packet.getPassengerIds().length > 1)); - } else { - EntityUtils.updateMountOffset(passenger, entity, (packet.getPassengerIds()[0] == passengerId), true, (packet.getPassengerIds().length > 1)); - } + newPassengers.add(passenger); + passenger.setVehicle(entity); + EntityUtils.updateRiderRotationLock(passenger, entity, true); + EntityUtils.updateMountOffset(passenger, entity, rider, true, (packet.getPassengerIds().length > 1)); // Force an update to the passenger metadata passenger.updateBedrockMetadata(); } + // Handle passengers that were removed + for (Entity passenger : entity.getPassengers()) { + if (passenger == null) { + continue; + } + if (!newPassengers.contains(passenger)) { + SetEntityLinkPacket linkPacket = new SetEntityLinkPacket(); + linkPacket.setEntityLink(new EntityLinkData(entity.getGeyserId(), passenger.getGeyserId(), EntityLinkData.Type.REMOVE, false, false)); + session.sendUpstreamPacket(linkPacket); + + passenger.setVehicle(null); + EntityUtils.updateRiderRotationLock(passenger, entity, false); + EntityUtils.updateMountOffset(passenger, entity, false, false, (packet.getPassengerIds().length > 1)); + // Force an update to the passenger metadata + passenger.updateBedrockMetadata(); + } + } + + entity.setPassengers(newPassengers); + switch (entity.getDefinition().entityType()) { case HORSE, SKELETON_HORSE, DONKEY, MULE, RAVAGER -> { entity.getDirtyMetadata().put(EntityData.RIDER_MAX_ROTATION, 181.0f); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaTakeItemEntityTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaTakeItemEntityTranslator.java index 75d7bc976..dbf335ceb 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaTakeItemEntityTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaTakeItemEntityTranslator.java @@ -49,12 +49,7 @@ public class JavaTakeItemEntityTranslator extends PacketTranslator { @@ -53,9 +53,10 @@ public class JavaPlayerPositionTranslator extends PacketTranslator 1); + vehicle.getPassengers().remove(entity); + session.getPlayerEntity().setVehicle(null); + + EntityUtils.updateRiderRotationLock(entity, null, false); + EntityUtils.updateMountOffset(entity, null, false, false, entity.getPassengers().size() > 1); + entity.updateBedrockMetadata(); } // If coordinates are relative, then add to the existing coordinate @@ -119,9 +118,9 @@ public class JavaPlayerPositionTranslator extends PacketTranslator mountedHeightOffset = height * 0.5f; case DONKEY, MULE -> mountedHeightOffset -= 0.25f; - case LLAMA -> mountedHeightOffset = height * 0.67f; + case TRADER_LLAMA, LLAMA -> mountedHeightOffset = height * 0.6f; case MINECART, HOPPER_MINECART, TNT_MINECART, CHEST_MINECART, FURNACE_MINECART, SPAWNER_MINECART, COMMAND_BLOCK_MINECART -> mountedHeightOffset = 0; case BOAT -> mountedHeightOffset = -0.1f; @@ -144,20 +145,22 @@ public final class EntityUtils { float yOffset = mountedHeightOffset + heightOffset; float zOffset = 0; switch (mount.getDefinition().entityType()) { - case BOAT: + case BOAT -> { // Without the X offset, more than one entity on a boat is stacked on top of each other if (rider && moreThanOneEntity) { xOffset = 0.2f; } else if (moreThanOneEntity) { xOffset = -0.6f; } - break; - case CHICKEN: - zOffset = -0.1f; - break; - case LLAMA: - zOffset = -0.3f; - break; + } + case CHICKEN -> zOffset = -0.1f; + case TRADER_LLAMA, LLAMA -> zOffset = -0.3f; + } + if (passenger.getDefinition().entityType() == EntityType.SHULKER) { + switch (mount.getDefinition().entityType()) { + case MINECART, HOPPER_MINECART, TNT_MINECART, CHEST_MINECART, FURNACE_MINECART, SPAWNER_MINECART, + COMMAND_BLOCK_MINECART, BOAT -> yOffset = 0.1875f; + } } /* * Bedrock Differences @@ -165,8 +168,10 @@ public final class EntityUtils { * Horses are tinier * Players, Minecarts, and Boats have different origins */ - if (passenger.getDefinition().entityType() == EntityType.PLAYER && mount.getDefinition().entityType() != EntityType.PLAYER) { - yOffset += EntityDefinitions.PLAYER.offset(); + if (passenger.getDefinition().entityType() == EntityType.PLAYER) { + if (mount.getDefinition().entityType() != EntityType.PLAYER && mount.getDefinition().entityType() != EntityType.AREA_EFFECT_CLOUD) { + yOffset += EntityDefinitions.PLAYER.offset(); + } } switch (mount.getDefinition().entityType()) { case MINECART, HOPPER_MINECART, TNT_MINECART, CHEST_MINECART, FURNACE_MINECART, SPAWNER_MINECART, @@ -175,7 +180,21 @@ public final class EntityUtils { Vector3f offset = Vector3f.from(xOffset, yOffset, zOffset); passenger.setRiderSeatPosition(offset); } - passenger.updateBedrockMetadata(); + } + + public static void updateRiderRotationLock(Entity passenger, Entity mount, boolean isRiding) { + if (isRiding && mount.getDefinition() == EntityDefinitions.BOAT) { + // Head rotation is locked while riding in a boat + passenger.getDirtyMetadata().put(EntityData.RIDER_ROTATION_LOCKED, (byte) 1); + passenger.getDirtyMetadata().put(EntityData.RIDER_MAX_ROTATION, 90f); + passenger.getDirtyMetadata().put(EntityData.RIDER_MIN_ROTATION, 1f); + passenger.getDirtyMetadata().put(EntityData.RIDER_ROTATION_OFFSET, -90f); + } else { + passenger.getDirtyMetadata().put(EntityData.RIDER_ROTATION_LOCKED, (byte) 0); + passenger.getDirtyMetadata().put(EntityData.RIDER_MAX_ROTATION, 0f); + passenger.getDirtyMetadata().put(EntityData.RIDER_MIN_ROTATION, 0f); + passenger.getDirtyMetadata().put(EntityData.RIDER_ROTATION_OFFSET, 0f); + } } private EntityUtils() {