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)
This commit is contained in:
David Choo 2021-12-18 11:43:57 -05:00 committed by GitHub
parent 90442a8922
commit 754bb42c19
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 263 additions and 366 deletions

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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<Entity> 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 extends Entity> I as(Class<I> entityClass) {
return entityClass.isInstance(this) ? (I) this : null;

View File

@ -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);

View File

@ -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);
}

View File

@ -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();
}
}

View File

@ -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 {

View File

@ -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) {

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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) {

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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<TeleportCache> 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<Int2ObjectMap.Entry<TeleportCache>> 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<TeleportCache> 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<TeleportCache> 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<Int2ObjectMap.Entry<TeleportCache>> 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<TeleportCache> 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);
}
}

View File

@ -56,15 +56,12 @@ public class EntityCache {
private final Long2LongMap entityIdTranslations = new Long2LongOpenHashMap();
private final Map<UUID, PlayerEntity> playerEntities = new Object2ObjectOpenHashMap<>();
private final Map<UUID, BossBar> 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<Tickable> getTickableEntities() {
return tickableEntities;
}

View File

@ -66,6 +66,10 @@ public class TeleportCache {
unconfirmedFor++;
}
public void resetUnconfirmedFor() {
unconfirmedFor = 0;
}
public boolean shouldResend() {
return unconfirmedFor >= RESEND_THRESHOLD;
}

View File

@ -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;
}

View File

@ -45,7 +45,7 @@ public class BedrockMoveEntityAbsoluteTranslator extends PacketTranslator<MoveEn
public void translate(GeyserSession session, MoveEntityAbsolutePacket packet) {
session.setLastVehicleMoveTimestamp(System.currentTimeMillis());
Entity ridingEntity = session.getRidingVehicleEntity();
Entity ridingEntity = session.getPlayerEntity().getVehicle();
if (ridingEntity != null && session.getWorldBorder().isPassingIntoBorderBoundaries(packet.getPosition(), false)) {
Vector3f position = Vector3f.from(ridingEntity.getPosition().getX(), packet.getPosition().getY(),
ridingEntity.getPosition().getZ());

View File

@ -54,7 +54,7 @@ public class BedrockPlayerInputTranslator extends PacketTranslator<PlayerInputPa
// Bedrock only sends movement vehicle packets while moving
// This allows horses to take damage while standing on magma
Entity vehicle = session.getRidingVehicleEntity();
Entity vehicle = session.getPlayerEntity().getVehicle();
boolean sendMovement = false;
if (vehicle instanceof AbstractHorseEntity && !(vehicle instanceof LlamaEntity)) {
sendMovement = vehicle.isOnGround();

View File

@ -77,16 +77,17 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
session.sendUpstreamPacket(attributesPacket);
break;
case START_SWIMMING:
ServerboundPlayerCommandPacket startSwimPacket = new ServerboundPlayerCommandPacket((int) entity.getEntityId(), PlayerState.START_SPRINTING);
session.sendDownstreamPacket(startSwimPacket);
session.setSwimming(true);
if (!session.getPlayerEntity().getFlag(EntityFlag.SWIMMING)) {
ServerboundPlayerCommandPacket startSwimPacket = new ServerboundPlayerCommandPacket((int) entity.getEntityId(), PlayerState.START_SPRINTING);
session.sendDownstreamPacket(startSwimPacket);
}
break;
case STOP_SWIMMING:
ServerboundPlayerCommandPacket stopSwimPacket = new ServerboundPlayerCommandPacket((int) entity.getEntityId(), PlayerState.STOP_SPRINTING);
session.sendDownstreamPacket(stopSwimPacket);
session.setSwimming(false);
// Prevent packet spam when Bedrock players are crawling near the edge of a block
if (session.isSwimmingInWater()) {
ServerboundPlayerCommandPacket stopSwimPacket = new ServerboundPlayerCommandPacket((int) entity.getEntityId(), PlayerState.STOP_SPRINTING);
session.sendDownstreamPacket(stopSwimPacket);
}
break;
case START_GLIDE:
// Otherwise gliding will not work in creative
@ -134,13 +135,17 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
session.setSneaking(false);
break;
case START_SPRINT:
ServerboundPlayerCommandPacket startSprintPacket = new ServerboundPlayerCommandPacket((int) entity.getEntityId(), PlayerState.START_SPRINTING);
session.sendDownstreamPacket(startSprintPacket);
session.setSprinting(true);
if (!session.getPlayerEntity().getFlag(EntityFlag.SWIMMING)) {
ServerboundPlayerCommandPacket startSprintPacket = new ServerboundPlayerCommandPacket((int) entity.getEntityId(), PlayerState.START_SPRINTING);
session.sendDownstreamPacket(startSprintPacket);
session.setSprinting(true);
}
break;
case STOP_SPRINT:
ServerboundPlayerCommandPacket stopSprintPacket = new ServerboundPlayerCommandPacket((int) entity.getEntityId(), PlayerState.STOP_SPRINTING);
session.sendDownstreamPacket(stopSprintPacket);
if (!session.getPlayerEntity().getFlag(EntityFlag.SWIMMING)) {
ServerboundPlayerCommandPacket stopSprintPacket = new ServerboundPlayerCommandPacket((int) entity.getEntityId(), PlayerState.STOP_SPRINTING);
session.sendDownstreamPacket(stopSprintPacket);
}
session.setSprinting(false);
break;
case DROP_ITEM:

View File

@ -74,7 +74,6 @@ public class BedrockInteractTranslator extends PacketTranslator<InteractPacket>
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<InteractPacket>
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

View File

@ -67,7 +67,8 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
// Send book update before the player moves
session.getBookEditCache().checkForSend();
if (!session.getTeleportMap().isEmpty()) {
// Ignore movement packets until Bedrock's position matches the teleported position
if (session.getUnconfirmedTeleport() != null) {
session.confirmTeleport(packet.getPosition().toDouble().sub(0, EntityDefinitions.PLAYER.offset(), 0));
return;
}

View File

@ -38,7 +38,7 @@ import org.geysermc.geyser.translator.protocol.Translator;
public class BedrockRiderJumpTranslator extends PacketTranslator<RiderJumpPacket> {
@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);

View File

@ -41,12 +41,7 @@ public class JavaAnimateTranslator extends PacketTranslator<ClientboundAnimatePa
@Override
public void translate(GeyserSession session, ClientboundAnimatePacket 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;

View File

@ -47,12 +47,7 @@ public class JavaEntityEventTranslator extends PacketTranslator<ClientboundEntit
@Override
public void translate(GeyserSession session, ClientboundEntityEventPacket 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;

View File

@ -37,9 +37,6 @@ public class JavaMoveEntityPosRotTranslator extends PacketTranslator<Clientbound
@Override
public void translate(GeyserSession session, ClientboundMoveEntityPosRotPacket packet) {
Entity entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId());
if (packet.getEntityId() == session.getPlayerEntity().getEntityId()) {
entity = session.getPlayerEntity();
}
if (entity == null) return;
entity.updatePositionAndRotation(packet.getMoveX(), packet.getMoveY(), packet.getMoveZ(), packet.getYaw(), packet.getPitch(), packet.isOnGround());

View File

@ -36,12 +36,7 @@ public class JavaMoveEntityPosTranslator extends PacketTranslator<ClientboundMov
@Override
public void translate(GeyserSession session, ClientboundMoveEntityPosPacket 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;
entity.moveRelative(packet.getMoveX(), packet.getMoveY(), packet.getMoveZ(), entity.getYaw(), entity.getPitch(), entity.getHeadYaw(), packet.isOnGround());

View File

@ -37,9 +37,6 @@ public class JavaMoveEntityRotTranslator extends PacketTranslator<ClientboundMov
@Override
public void translate(GeyserSession session, ClientboundMoveEntityRotPacket packet) {
Entity entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId());
if (packet.getEntityId() == session.getPlayerEntity().getEntityId()) {
entity = session.getPlayerEntity();
}
if (entity == null) return;
entity.updateRotation(packet.getYaw(), packet.getPitch(), packet.isOnGround());

View File

@ -37,7 +37,7 @@ public class JavaMoveVehicleTranslator extends PacketTranslator<ClientboundMoveV
@Override
public void translate(GeyserSession session, ClientboundMoveVehiclePacket packet) {
Entity entity = session.getRidingVehicleEntity();
Entity entity = session.getPlayerEntity().getVehicle();
if (entity == null) return;
entity.moveAbsolute(Vector3f.from(packet.getX(), packet.getY(), packet.getZ()), packet.getYaw(), packet.getPitch(), false, true);

View File

@ -38,12 +38,9 @@ public class JavaRemoveMobEffectTranslator extends PacketTranslator<ClientboundR
@Override
public void translate(GeyserSession session, ClientboundRemoveMobEffectPacket packet) {
Entity entity;
if (packet.getEntityId() == session.getPlayerEntity().getEntityId()) {
entity = session.getPlayerEntity();
Entity entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId());
if (entity == session.getPlayerEntity()) {
session.getEffectCache().removeEffect(packet.getEffect());
} else {
entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId());
}
if (entity == null)
return;

View File

@ -36,13 +36,7 @@ public class JavaRotateHeadTranslator extends PacketTranslator<ClientboundRotate
@Override
public void translate(GeyserSession session, ClientboundRotateHeadPacket 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;
entity.updateHeadLookRotation(packet.getHeadYaw());

View File

@ -40,12 +40,7 @@ public class JavaSetEntityDataTranslator extends PacketTranslator<ClientboundSet
@SuppressWarnings({"rawtypes", "unchecked"})
@Override
public void translate(GeyserSession session, ClientboundSetEntityDataPacket 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;
EntityDefinition<?> definition = entity.getDefinition();

View File

@ -43,33 +43,23 @@ public class JavaSetEntityLinkTranslator extends PacketTranslator<ClientboundSet
@Override
public void translate(GeyserSession session, ClientboundSetEntityLinkPacket packet) {
Entity holderId;
if (packet.getEntityId() == session.getPlayerEntity().getEntityId()) {
holderId = session.getPlayerEntity();
} else {
holderId = session.getEntityCache().getEntityByJavaId(packet.getEntityId());
}
Entity holderId = session.getEntityCache().getEntityByJavaId(packet.getEntityId());
if (!(holderId instanceof MobEntity mobEntity)) {
return;
}
Entity attachedToId;
if (packet.getAttachedToId() == session.getPlayerEntity().getEntityId()) {
attachedToId = session.getPlayerEntity();
} else {
attachedToId = session.getEntityCache().getEntityByJavaId(packet.getAttachedToId());
if ((attachedToId == null || packet.getAttachedToId() == 0)) {
// Is not being leashed
mobEntity.setFlag(EntityFlag.LEASHED, false);
mobEntity.setLeashHolderBedrockId(-1L);
mobEntity.updateBedrockMetadata();
EntityEventPacket eventPacket = new EntityEventPacket();
eventPacket.setRuntimeEntityId(holderId.getGeyserId());
eventPacket.setType(EntityEventType.REMOVE_LEASH);
eventPacket.setData(0);
session.sendUpstreamPacket(eventPacket);
return;
}
Entity attachedToId = session.getEntityCache().getEntityByJavaId(packet.getAttachedToId());
if (attachedToId == null || packet.getAttachedToId() == 0) {
// Is not being leashed
mobEntity.setFlag(EntityFlag.LEASHED, false);
mobEntity.setLeashHolderBedrockId(-1L);
mobEntity.updateBedrockMetadata();
EntityEventPacket eventPacket = new EntityEventPacket();
eventPacket.setRuntimeEntityId(holderId.getGeyserId());
eventPacket.setType(EntityEventType.REMOVE_LEASH);
eventPacket.setData(0);
session.sendUpstreamPacket(eventPacket);
return;
}
mobEntity.setFlag(EntityFlag.LEASHED, true);

View File

@ -41,17 +41,12 @@ public class JavaSetEntityMotionTranslator extends PacketTranslator<ClientboundS
@Override
public void translate(GeyserSession session, ClientboundSetEntityMotionPacket 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;
entity.setMotion(Vector3f.from(packet.getMotionX(), packet.getMotionY(), packet.getMotionZ()));
if (entity == session.getRidingVehicleEntity() && entity instanceof AbstractHorseEntity) {
if (entity == session.getPlayerEntity().getVehicle() && entity instanceof AbstractHorseEntity) {
// Horses for some reason teleport back when a SetEntityMotionPacket is sent while
// a player is riding on them. Java clients seem to ignore it anyways.
return;

View File

@ -44,13 +44,7 @@ public class JavaSetEquipmentTranslator extends PacketTranslator<ClientboundSetE
@Override
public void translate(GeyserSession session, ClientboundSetEquipmentPacket 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;

View File

@ -29,100 +29,73 @@ import com.github.steveice10.mc.protocol.packet.ingame.clientbound.entity.Client
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.data.entity.EntityLinkData;
import com.nukkitx.protocol.bedrock.packet.SetEntityLinkPacket;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import org.geysermc.geyser.entity.type.Entity;
import org.geysermc.geyser.entity.EntityDefinitions;
import org.geysermc.geyser.entity.type.Entity;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.geyser.util.EntityUtils;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
@Translator(packet = ClientboundSetPassengersPacket.class)
public class JavaSetPassengersTranslator extends PacketTranslator<ClientboundSetPassengersPacket> {
@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<Entity> 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);

View File

@ -49,12 +49,7 @@ public class JavaTakeItemEntityTranslator extends PacketTranslator<ClientboundTa
Entity collectedEntity = session.getEntityCache().getEntityByJavaId(packet.getCollectedEntityId());
if (collectedEntity == null) return;
// Collector is the entity 'picking up' the item
Entity collectorEntity;
if (packet.getCollectorEntityId() == session.getPlayerEntity().getEntityId()) {
collectorEntity = session.getPlayerEntity();
} else {
collectorEntity = session.getEntityCache().getEntityByJavaId(packet.getCollectorEntityId());
}
Entity collectorEntity = session.getEntityCache().getEntityByJavaId(packet.getCollectorEntityId());
if (collectorEntity == null) return;
if (collectedEntity instanceof ExpOrbEntity) {
// Player just picked up an experience orb

View File

@ -38,9 +38,6 @@ public class JavaTeleportEntityTranslator extends PacketTranslator<ClientboundTe
@Override
public void translate(GeyserSession session, ClientboundTeleportEntityPacket packet) {
Entity entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId());
if (packet.getEntityId() == session.getPlayerEntity().getEntityId()) {
entity = session.getPlayerEntity();
}
if (entity == null) return;
entity.teleport(Vector3f.from(packet.getX(), packet.getY(), packet.getZ()), packet.getYaw(), packet.getPitch(), packet.isOnGround());

View File

@ -37,12 +37,7 @@ public class JavaUpdateAttributesTranslator extends PacketTranslator<Clientbound
@Override
public void translate(GeyserSession session, ClientboundUpdateAttributesPacket 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 instanceof LivingEntity livingEntity)) return;
livingEntity.updateBedrockAttributes(session, packet.getAttributes());

View File

@ -38,12 +38,9 @@ public class JavaUpdateMobEffectTranslator extends PacketTranslator<ClientboundU
@Override
public void translate(GeyserSession session, ClientboundUpdateMobEffectPacket packet) {
Entity entity;
if (packet.getEntityId() == session.getPlayerEntity().getEntityId()) {
entity = session.getPlayerEntity();
Entity entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId());
if (entity == session.getPlayerEntity()) {
session.getEffectCache().setEffect(packet.getEffect(), packet.getAmplifier());
} else {
entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId());
}
if (entity == null)
return;

View File

@ -28,22 +28,22 @@ package org.geysermc.geyser.translator.protocol.java.entity.player;
import com.github.steveice10.mc.protocol.data.game.entity.player.PositionElement;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.entity.player.ClientboundPlayerPositionPacket;
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.level.ServerboundAcceptTeleportationPacket;
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerPosRotPacket;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.data.entity.EntityLinkData;
import com.nukkitx.protocol.bedrock.packet.MovePlayerPacket;
import com.nukkitx.protocol.bedrock.packet.RespawnPacket;
import com.nukkitx.protocol.bedrock.packet.SetEntityLinkPacket;
import org.geysermc.geyser.entity.type.Entity;
import org.geysermc.geyser.entity.EntityDefinitions;
import org.geysermc.geyser.entity.type.player.PlayerEntity;
import org.geysermc.geyser.entity.type.Entity;
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.TeleportCache;
import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.geyser.util.ChunkUtils;
import org.geysermc.geyser.util.EntityUtils;
import org.geysermc.geyser.text.GeyserLocale;
@Translator(packet = ClientboundPlayerPositionPacket.class)
public class JavaPlayerPositionTranslator extends PacketTranslator<ClientboundPlayerPositionPacket> {
@ -53,9 +53,10 @@ public class JavaPlayerPositionTranslator extends PacketTranslator<ClientboundPl
if (!session.isLoggedIn())
return;
PlayerEntity entity = session.getPlayerEntity();
SessionPlayerEntity entity = session.getPlayerEntity();
if (!session.isSpawned()) {
// The server sends an absolute teleport everytime the player is respawned
Vector3f pos = Vector3f.from(packet.getX(), packet.getY(), packet.getZ());
entity.setPosition(pos);
entity.setYaw(packet.getYaw());
@ -78,9 +79,10 @@ public class JavaPlayerPositionTranslator extends PacketTranslator<ClientboundPl
session.sendUpstreamPacket(movePlayerPacket);
session.setSpawned(true);
// Make sure the player moves away from (0, 32767, 0) before accepting movement packets
session.setUnconfirmedTeleport(new TeleportCache(packet.getX(), packet.getY(), packet.getZ(), packet.getPitch(), packet.getYaw(), packet.getTeleportId()));
ServerboundAcceptTeleportationPacket teleportConfirmPacket = new ServerboundAcceptTeleportationPacket(packet.getTeleportId());
session.sendDownstreamPacket(teleportConfirmPacket);
acceptTeleport(session, packet.getX(), packet.getY(), packet.getZ(), packet.getYaw(), packet.getPitch(), packet.getTeleportId());
ChunkUtils.updateChunkPosition(session, pos.toInt());
@ -88,22 +90,19 @@ public class JavaPlayerPositionTranslator extends PacketTranslator<ClientboundPl
return;
}
session.setSpawned(true);
Entity vehicle = session.getPlayerEntity().getVehicle();
if (packet.isDismountVehicle() && vehicle != null) {
if (packet.isDismountVehicle() && session.getRidingVehicleEntity() != null) {
Entity vehicle = session.getRidingVehicleEntity();
SetEntityLinkPacket linkPacket = new SetEntityLinkPacket();
linkPacket.setEntityLink(new EntityLinkData(vehicle.getGeyserId(), entity.getGeyserId(), EntityLinkData.Type.REMOVE, false, false));
session.sendUpstreamPacket(linkPacket);
vehicle.getPassengers().remove(entity.getEntityId());
entity.getDirtyMetadata().put(EntityData.RIDER_ROTATION_LOCKED, (byte) 0);
entity.getDirtyMetadata().put(EntityData.RIDER_MAX_ROTATION, 0f);
entity.getDirtyMetadata().put(EntityData.RIDER_MIN_ROTATION, 0f);
entity.getDirtyMetadata().put(EntityData.RIDER_ROTATION_OFFSET, 0f);
session.setRidingVehicleEntity(null);
entity.updateBedrockMetadata();
EntityUtils.updateMountOffset(entity, vehicle, false, false, entity.getPassengers().size() > 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<ClientboundPl
float newYaw = packet.getYaw() +
(packet.getRelative().contains(PositionElement.YAW) ? entity.getBedrockRotation().getY() : 0);
session.getGeyser().getLogger().debug("Teleport from " + entity.getPosition().getX() + " " + (entity.getPosition().getY() - EntityDefinitions.PLAYER.offset()) + " " + entity.getPosition().getZ());
int id = packet.getTeleportId();
session.addTeleport(new TeleportCache(newX, newY, newZ, newPitch, newYaw, packet.getTeleportId()));
session.getGeyser().getLogger().debug("Teleport (" + id + ") from " + entity.getPosition().getX() + " " + (entity.getPosition().getY() - EntityDefinitions.PLAYER.offset()) + " " + entity.getPosition().getZ());
Vector3f lastPlayerPosition = entity.getPosition().down(EntityDefinitions.PLAYER.offset());
float lastPlayerPitch = entity.getBedrockRotation().getX();
@ -131,9 +130,22 @@ public class JavaPlayerPositionTranslator extends PacketTranslator<ClientboundPl
session.getGeyser().getLogger().debug("to " + entity.getPosition().getX() + " " + (entity.getPosition().getY() - EntityDefinitions.PLAYER.offset()) + " " + entity.getPosition().getZ());
// Bedrock ignores teleports that are extremely close to the player's original position and orientation,
// so check if we can immediately confirm the teleport
// so check if we need to cache the teleport
if (lastPlayerPosition.distanceSquared(teleportDestination) < 0.001 && Math.abs(newPitch - lastPlayerPitch) < 5) {
session.confirmTeleport(lastPlayerPosition.toDouble());
session.setUnconfirmedTeleport(null);
} else {
session.setUnconfirmedTeleport(new TeleportCache(newX, newY, newZ, newPitch, newYaw, id));
}
acceptTeleport(session, newX, newY, newZ, newYaw, newPitch, id);
}
private void acceptTeleport(GeyserSession session, double x, double y, double z, float yaw, float pitch, int id) {
// Confirm the teleport when we receive it to match Java edition
ServerboundAcceptTeleportationPacket teleportConfirmPacket = new ServerboundAcceptTeleportationPacket(id);
session.sendDownstreamPacket(teleportConfirmPacket);
// Servers (especially ones like Hypixel) expect exact coordinates given back to them.
ServerboundMovePlayerPosRotPacket positionPacket = new ServerboundMovePlayerPosRotPacket(false, x, y, z, yaw, pitch);
session.sendDownstreamPacket(positionPacket);
}
}

View File

@ -71,12 +71,7 @@ public class JavaAddEntityTranslator extends PacketTranslator<ClientboundAddEnti
} else if (packet.getType() == EntityType.FISHING_BOBBER) {
// Fishing bobbers need the owner for the line
int ownerEntityId = ((ProjectileData) packet.getData()).getOwnerId();
Entity owner;
if (session.getPlayerEntity().getEntityId() == ownerEntityId) {
owner = session.getPlayerEntity();
} else {
owner = session.getEntityCache().getEntityByJavaId(ownerEntityId);
}
Entity owner = session.getEntityCache().getEntityByJavaId(ownerEntityId);
// Java clients only spawn fishing hooks with a player as its owner
if (owner instanceof PlayerEntity) {
entity = new FishingHookEntity(session, packet.getEntityId(), session.getEntityCache().getNextEntityId().incrementAndGet(), packet.getUuid(),

View File

@ -28,6 +28,7 @@ package org.geysermc.geyser.util;
import com.github.steveice10.mc.protocol.data.game.entity.Effect;
import com.github.steveice10.mc.protocol.data.game.entity.type.EntityType;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.geyser.entity.type.Entity;
import org.geysermc.geyser.entity.EntityDefinitions;
@ -74,7 +75,7 @@ public final class EntityUtils {
switch (mount.getDefinition().entityType()) {
case CHICKEN, SPIDER -> 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() {