//  [java]
    public void translate(GeyserSession session, ServerPlayerPositionRotationPacket packet) {
        if (!session.isLoggedIn())
            return;

        PlayerEntity entity = session.getPlayerEntity();

        if (!session.isSpawned()) {
            Vector3f pos = Vector3f.from(packet.getX(), packet.getY(), packet.getZ());
            entity.setPosition(pos);
            entity.setRotation(Vector3f.from(packet.getYaw(), packet.getPitch(), packet.getYaw()));

            RespawnPacket respawnPacket = new RespawnPacket();
            respawnPacket.setRuntimeEntityId(0); // Bedrock server behavior
            respawnPacket.setPosition(entity.getPosition());
            respawnPacket.setState(RespawnPacket.State.SERVER_READY);
            session.sendUpstreamPacket(respawnPacket);

            SetEntityDataPacket entityDataPacket = new SetEntityDataPacket();
            entityDataPacket.setRuntimeEntityId(entity.getGeyserId());
            entityDataPacket.getMetadata().putAll(entity.getMetadata());
            session.sendUpstreamPacket(entityDataPacket);

            MovePlayerPacket movePlayerPacket = new MovePlayerPacket();
            movePlayerPacket.setRuntimeEntityId(entity.getGeyserId());
            movePlayerPacket.setPosition(entity.getPosition());
            movePlayerPacket.setRotation(Vector3f.from(packet.getPitch(), packet.getYaw(), 0));
            movePlayerPacket.setMode(MovePlayerPacket.Mode.RESPAWN);

            session.sendUpstreamPacket(movePlayerPacket);
            session.setSpawned(true);

            ClientTeleportConfirmPacket teleportConfirmPacket = new ClientTeleportConfirmPacket(packet.getTeleportId());
            session.sendDownstreamPacket(teleportConfirmPacket);

            ChunkUtils.updateChunkPosition(session, pos.toInt());

            session.getConnector().getLogger().debug(LanguageUtils.getLocaleStringLog("geyser.entity.player.spawn", packet.getX(), packet.getY(), packet.getZ()));
            return;
        }

        session.setSpawned(true);

        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.getMetadata().put(EntityData.RIDER_ROTATION_LOCKED, (byte) 0);
            entity.getMetadata().put(EntityData.RIDER_MAX_ROTATION, 0f);
            entity.getMetadata().put(EntityData.RIDER_MIN_ROTATION, 0f);
            entity.getMetadata().put(EntityData.RIDER_ROTATION_OFFSET, 0f);
            session.setRidingVehicleEntity(null);
            entity.updateBedrockMetadata(session);

            EntityUtils.updateMountOffset(entity, vehicle, session, false, false, entity.getPassengers().size() > 1);
        }

        // If coordinates are relative, then add to the existing coordinate
        double newX = packet.getX() +
                (packet.getRelative().contains(PositionElement.X) ? entity.getPosition().getX() : 0);
        double newY = packet.getY() +
                (packet.getRelative().contains(PositionElement.Y) ? entity.getPosition().getY() - EntityType.PLAYER.getOffset() : 0);
        double newZ = packet.getZ() +
                (packet.getRelative().contains(PositionElement.Z) ? entity.getPosition().getZ() : 0);

        float newPitch = packet.getPitch() +
                (packet.getRelative().contains(PositionElement.PITCH) ? entity.getBedrockRotation().getX() : 0);
        float newYaw = packet.getYaw() +
                (packet.getRelative().contains(PositionElement.YAW) ? entity.getBedrockRotation().getY() : 0);

        session.getConnector().getLogger().debug("Teleport from " + entity.getPosition().getX() + " " + (entity.getPosition().getY() - EntityType.PLAYER.getOffset()) + " " + entity.getPosition().getZ());

        session.addTeleport(new TeleportCache(newX, newY, newZ, newPitch, newYaw, packet.getTeleportId()));

        Vector3f lastPlayerPosition = entity.getPosition().down(EntityType.PLAYER.getOffset());
        float lastPlayerPitch = entity.getBedrockRotation().getX();
        Vector3f teleportDestination = Vector3f.from(newX, newY, newZ);
        entity.moveAbsolute(session, teleportDestination, newYaw, newPitch, true, true);

        session.getConnector().getLogger().debug("to " + entity.getPosition().getX() + " " + (entity.getPosition().getY() - EntityType.PLAYER.getOffset()) + " " + 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
        if (lastPlayerPosition.distanceSquared(teleportDestination) < 0.001 && Math.abs(newPitch - lastPlayerPitch) < 5) {
            session.confirmTeleport(lastPlayerPosition.toDouble());
        }
    }