Repository: Pumpkin-MC/Pumpkin
Pull request #742: Fix position desync between clients
Author: login: Alvsch
Created: 2025-04-15T21:12:54Z | Merged: 2025-04-24T12:38:03Z

<!-- Empty or bad Descriptions are not welcome, Don't waste my time -->

## Description
This PR makes sure the player position isn't desynced between players when moving too fast (>8 blocks/tick).
Also fixes `/tp` desync (Fixes #363)  

## Testing
Two minecraft players looking at each other
- `/tp ~ ~10 ~` and make sure no weird stuff happens
- Move faster than 8 blocks and make sure no weird stuff happens (hacked clients are good for this)


Please follow our [Coding Guidelines](https://github.com/Pumpkin-MC/Pumpkin/blob/master/CONTRIBUTING.md#coding-guidelines)


Linked issue #363: Teleport de-sync
### I've searched existing issues and couldn't find a duplicate.

- [X] I confirm this is not a duplicate.

### Operating System

Manjaro Linux

### Server Software Version/Commit

f29cfbc

### What happened?

Player position gets de-synced between clients when teleporting more than 8 blocks. This results in the player falling through the ground on other players POV.

### To Reproduce

/teleport ~ ~10 ~
You should then be in the ground for other players when falling to the ground.

### Expected behavior

Teleport 10 blocks up and then fall down stopping at the ground for all players.
Linked issue comments: user: login: Commandcracker
created_at: 2024-12-01T20:13:29Z
body: Can confirm 👍 
author_association: CONTRIBUTOR
user: login: DataM0del
created_at: 2024-12-06T22:34:22Z
body: I think I've had this months ago.
author_association: CONTRIBUTOR
user: login: Snowiiii
created_at: 2024-12-07T14:41:20Z
body: I think it is because of the relative PositionFlags which are not being send (we just use an empty array)
author_association: MEMBER
user: login: Witcher01
created_at: 2025-01-12T12:32:20Z
body: @Snowiiii I don't think that's the case:

> In the lower 8 bits of the bit field, a set bit means the teleportation on the corresponding axis is relative, and an unset bit that it is absolute.

https://minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/Protocol#Teleport_Flags

Not setting any bits there means the teleportation is absolute, not relative. The teleportation itself works because the coordinates always seem to be given as absolute ones. All argument consumers used for teleporation with positions are `Position3DArgumentConsumer`s, which convert the relative position to an absolute one when the arguments are parsed in the dispatcher. Given that teleporting works in principal, even with relative coordinates, this *shouldn't* be the issue from what I can see.

Setting some of the bits didn't solve the issue for me, either.

I think it's fair to remove the "good first issue" label from this one.
author_association: NONE

Diff:
diff --git a/pumpkin-protocol/src/client/play/mod.rs b/pumpkin-protocol/src/client/play/mod.rs
index 8cd4de5479..d3c72ce890 100644
--- a/pumpkin-protocol/src/client/play/mod.rs
+++ b/pumpkin-protocol/src/client/play/mod.rs
@@ -68,6 +68,7 @@ mod spawn_entity;
 mod stop_sound;
 mod store_cookie;
 mod subtitle;
+mod synchronize_vehicle_position;
 mod system_chat_message;
 mod take_item;
 mod teleport_entity;
@@ -151,6 +152,7 @@ pub use spawn_entity::*;
 pub use stop_sound::*;
 pub use store_cookie::*;
 pub use subtitle::*;
+pub use synchronize_vehicle_position::*;
 pub use system_chat_message::*;
 pub use take_item::*;
 pub use teleport_entity::*;
diff --git a/pumpkin-protocol/src/client/play/synchronize_vehicle_position.rs b/pumpkin-protocol/src/client/play/synchronize_vehicle_position.rs
new file mode 100644
index 0000000000..37ff69e06f
--- /dev/null
+++ b/pumpkin-protocol/src/client/play/synchronize_vehicle_position.rs
@@ -0,0 +1,65 @@
+use std::io::Write;
+
+use pumpkin_data::packet::clientbound::PLAY_TELEPORT_ENTITY;
+use pumpkin_macros::packet;
+use pumpkin_util::math::vector3::Vector3;
+
+use crate::{
+    ClientPacket, PositionFlag, VarInt,
+    ser::{NetworkWriteExt, WritingError},
+};
+
+// https://minecraft.wiki/w/Java_Edition_protocol#Synchronize_Vehicle_Position
+// Badly documented and confusing packet imo
+#[packet(PLAY_TELEPORT_ENTITY)]
+pub struct CSynchronizeVehiclePosition<'a> {
+    entity_id: VarInt,
+    position: Vector3<f64>,
+    delta: Vector3<f64>,
+    yaw: f32,
+    pitch: f32,
+    relatives: &'a [PositionFlag],
+    on_ground: bool,
+}
+
+impl<'a> CSynchronizeVehiclePosition<'a> {
+    pub fn new(
+        entity_id: VarInt,
+        position: Vector3<f64>,
+        delta: Vector3<f64>,
+        yaw: f32,
+        pitch: f32,
+        relatives: &'a [PositionFlag],
+        on_ground: bool,
+    ) -> Self {
+        Self {
+            entity_id,
+            position,
+            delta,
+            yaw,
+            pitch,
+            relatives,
+            on_ground,
+        }
+    }
+}
+
+// TODO: Do we need a custom impl?
+impl ClientPacket for CSynchronizeVehiclePosition<'_> {
+    fn write_packet_data(&self, write: impl Write) -> Result<(), WritingError> {
+        let mut write = write;
+
+        write.write_var_int(&self.entity_id)?;
+        write.write_f64_be(self.position.x)?;
+        write.write_f64_be(self.position.y)?;
+        write.write_f64_be(self.position.z)?;
+        write.write_f64_be(self.delta.x)?;
+        write.write_f64_be(self.delta.y)?;
+        write.write_f64_be(self.delta.z)?;
+        write.write_f32_be(self.yaw)?;
+        write.write_f32_be(self.pitch)?;
+        // not sure about that
+        write.write_i32_be(PositionFlag::get_bitfield(self.relatives))?;
+        write.write_bool(self.on_ground)
+    }
+}
diff --git a/pumpkin-protocol/src/client/play/teleport_entity.rs b/pumpkin-protocol/src/client/play/teleport_entity.rs
index 082159c8ea..5c5b655cd8 100644
--- a/pumpkin-protocol/src/client/play/teleport_entity.rs
+++ b/pumpkin-protocol/src/client/play/teleport_entity.rs
@@ -1,33 +1,30 @@
-use std::io::Write;
-
-use pumpkin_data::packet::clientbound::PLAY_TELEPORT_ENTITY;
+use pumpkin_data::packet::clientbound::PLAY_ENTITY_POSITION_SYNC;
 use pumpkin_macros::packet;
 use pumpkin_util::math::vector3::Vector3;
+use serde::Serialize;
 
-use crate::{
-    ClientPacket, PositionFlag, VarInt,
-    ser::{NetworkWriteExt, WritingError},
-};
+use crate::VarInt;
 
-#[packet(PLAY_TELEPORT_ENTITY)]
-pub struct CTeleportEntity<'a> {
+// https://minecraft.wiki/w/Java_Edition_protocol#Teleport_Entity
+// Badly documented and confusing packet imo
+#[packet(PLAY_ENTITY_POSITION_SYNC)]
+#[derive(Serialize)]
+pub struct CTeleportEntity {
     entity_id: VarInt,
     position: Vector3<f64>,
     delta: Vector3<f64>,
     yaw: f32,
     pitch: f32,
-    relatives: &'a [PositionFlag],
     on_ground: bool,
 }
 
-impl<'a> CTeleportEntity<'a> {
+impl CTeleportEntity {
     pub fn new(
         entity_id: VarInt,
         position: Vector3<f64>,
         delta: Vector3<f64>,
         yaw: f32,
         pitch: f32,
-        relatives: &'a [PositionFlag],
         on_ground: bool,
     ) -> Self {
         Self {
@@ -36,28 +33,7 @@ impl<'a> CTeleportEntity<'a> {
             delta,
             yaw,
             pitch,
-            relatives,
             on_ground,
         }
     }
 }
-
-// TODO: Do we need a custom impl?
-impl ClientPacket for CTeleportEntity<'_> {
-    fn write_packet_data(&self, write: impl Write) -> Result<(), WritingError> {
-        let mut write = write;
-
-        write.write_var_int(&self.entity_id)?;
-        write.write_f64_be(self.position.x)?;
-        write.write_f64_be(self.position.y)?;
-        write.write_f64_be(self.position.z)?;
-        write.write_f64_be(self.delta.x)?;
-        write.write_f64_be(self.delta.y)?;
-        write.write_f64_be(self.delta.z)?;
-        write.write_f32_be(self.yaw)?;
-        write.write_f32_be(self.pitch)?;
-        // not sure about that
-        write.write_i32_be(PositionFlag::get_bitfield(self.relatives))?;
-        write.write_bool(self.on_ground)
-    }
-}
diff --git a/pumpkin-protocol/src/lib.rs b/pumpkin-protocol/src/lib.rs
index f77457d5df..6f3bef9a1a 100644
--- a/pumpkin-protocol/src/lib.rs
+++ b/pumpkin-protocol/src/lib.rs
@@ -410,6 +410,7 @@ pub enum NumberFormat {
     Fixed(TextComponent),
 }
 
+/// For the first 8 values set means relative value while unset means absolute
 #[derive(Debug, PartialEq, Eq, Hash)]
 pub enum PositionFlag {
     X,
diff --git a/pumpkin/src/command/commands/teleport.rs b/pumpkin/src/command/commands/teleport.rs
index 98beb7f4e7..4f7afcf77c 100644
--- a/pumpkin/src/command/commands/teleport.rs
+++ b/pumpkin/src/command/commands/teleport.rs
@@ -67,7 +67,7 @@ impl CommandExecutor for EntitiesToEntityExecutor {
         for target in targets {
             let yaw = target.living_entity.entity.yaw.load();
             let pitch = target.living_entity.entity.pitch.load();
-            target.living_entity.entity.teleport(pos, yaw, pitch).await;
+            target.teleport(pos, yaw, pitch).await;
         }
 
         Ok(())
@@ -92,7 +92,7 @@ impl CommandExecutor for EntitiesToPosFacingPosExecutor {
         let (yaw, pitch) = yaw_pitch_facing_position(&pos, &facing_pos);
 
         for target in targets {
-            target.living_entity.entity.teleport(pos, yaw, pitch).await;
+            target.teleport(pos, yaw, pitch).await;
         }
 
         Ok(())
@@ -119,7 +119,7 @@ impl CommandExecutor for EntitiesToPosFacingEntityExecutor {
         let (yaw, pitch) = yaw_pitch_facing_position(&pos, &facing_entity.pos.load());
 
         for target in targets {
-            target.living_entity.entity.teleport(pos, yaw, pitch).await;
+            target.teleport(pos, yaw, pitch).await;
         }
 
         Ok(())
@@ -143,7 +143,7 @@ impl CommandExecutor for EntitiesToPosWithRotationExecutor {
         let (yaw, pitch) = RotationArgumentConsumer::find_arg(args, ARG_ROTATION)?;
 
         for target in targets {
-            target.living_entity.entity.teleport(pos, yaw, pitch).await;
+            target.teleport(pos, yaw, pitch).await;
         }
 
         Ok(())
@@ -167,7 +167,7 @@ impl CommandExecutor for EntitiesToPosExecutor {
         for target in targets {
             let yaw = target.living_entity.entity.yaw.load();
             let pitch = target.living_entity.entity.pitch.load();
-            target.living_entity.entity.teleport(pos, yaw, pitch).await;
+            target.teleport(pos, yaw, pitch).await;
         }
 
         Ok(())
@@ -191,7 +191,7 @@ impl CommandExecutor for SelfToEntityExecutor {
             CommandSender::Player(player) => {
                 let yaw = player.living_entity.entity.yaw.load();
                 let pitch = player.living_entity.entity.pitch.load();
-                player.living_entity.entity.teleport(pos, yaw, pitch).await;
+                player.teleport(pos, yaw, pitch).await;
             }
             _ => {
                 sender
@@ -219,7 +219,7 @@ impl CommandExecutor for SelfToPosExecutor {
                 let pos = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?;
                 let yaw = player.living_entity.entity.yaw.load();
                 let pitch = player.living_entity.entity.pitch.load();
-                player.living_entity.entity.teleport(pos, yaw, pitch).await;
+                player.teleport(pos, yaw, pitch).await;
             }
             _ => {
                 sender
diff --git a/pumpkin/src/entity/mod.rs b/pumpkin/src/entity/mod.rs
index 09803af02b..8df1dcfc48 100644
--- a/pumpkin/src/entity/mod.rs
+++ b/pumpkin/src/entity/mod.rs
@@ -281,8 +281,7 @@ impl Entity {
                 yaw,
                 pitch,
                 // TODO
-                &[],
-                self.on_ground.load(std::sync::atomic::Ordering::SeqCst),
+                self.on_ground.load(Ordering::SeqCst),
             ))
             .await;
         self.set_pos(position);
diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs
index 4dea1cf754..228fd2902e 100644
--- a/pumpkin/src/entity/player.rs
+++ b/pumpkin/src/entity/player.rs
@@ -48,15 +48,17 @@ use pumpkin_inventory::player::{
 use pumpkin_macros::send_cancellable;
 use pumpkin_nbt::compound::NbtCompound;
 use pumpkin_nbt::tag::NbtTag;
-use pumpkin_protocol::client::play::{CSetHeldItem, PlayerInfoFlags, PreviousMessage};
+use pumpkin_protocol::client::play::{
+    CSetHeldItem, CTeleportEntity, PlayerInfoFlags, PreviousMessage,
+};
 use pumpkin_protocol::{
     IdOr, RawPacket, ServerPacket,
     client::play::{
         CAcknowledgeBlockChange, CActionBar, CChunkBatchEnd, CChunkBatchStart, CChunkData,
         CCombatDeath, CDisguisedChatMessage, CGameEvent, CKeepAlive, CParticle, CPlayDisconnect,
         CPlayerAbilities, CPlayerInfoUpdate, CPlayerPosition, CRespawn, CSetExperience, CSetHealth,
-        CStopSound, CSubtitle, CSystemChatMessage, CTeleportEntity, CTitleText, CUnloadChunk,
-        CUpdateMobEffect, GameEvent, MetaDataType, PlayerAction,
+        CStopSound, CSubtitle, CSystemChatMessage, CTitleText, CUnloadChunk, CUpdateMobEffect,
+        GameEvent, MetaDataType, PlayerAction,
     },
     codec::identifier::Identifier,
     ser::packet::Packet,
@@ -951,30 +953,23 @@ impl Player {
                 to: position,
                 cancelled: false,
             };
-
             'after: {
                 let position = event.to;
-                self.living_entity
-                    .entity
+                let entity = self.get_entity();
+                self.request_teleport(position, yaw, pitch).await;
+                entity
                     .world
                     .read()
                     .await
-                    .broadcast_packet_all(&CTeleportEntity::new(
+                    .broadcast_packet_except(&[self.gameprofile.id], &CTeleportEntity::new(
                         self.living_entity.entity.entity_id.into(),
                         position,
                         Vector3::new(0.0, 0.0, 0.0),
                         yaw,
                         pitch,
-                        // TODO
-                        &[],
-                        self.living_entity
-                            .entity
-                            .on_ground
-                            .load(std::sync::atomic::Ordering::SeqCst),
+                        entity.on_ground.load(Ordering::SeqCst),
                     ))
                     .await;
-                self.living_entity.entity.set_pos(position);
-                self.living_entity.entity.set_rotation(yaw, pitch);
             }
         }}
     }
diff --git a/pumpkin/src/net/packet/play.rs b/pumpkin/src/net/packet/play.rs
index df08640008..f5b4eeca0b 100644
--- a/pumpkin/src/net/packet/play.rs
+++ b/pumpkin/src/net/packet/play.rs
@@ -4,6 +4,7 @@ use sha1::Sha1;
 
 use std::num::NonZeroU8;
 use std::sync::Arc;
+use std::sync::atomic::Ordering;
 use std::time::{SystemTime, UNIX_EPOCH};
 
 use crate::block;
@@ -15,7 +16,7 @@ use crate::plugin::player::player_chat::PlayerChatEvent;
 use crate::plugin::player::player_command_send::PlayerCommandSendEvent;
 use crate::plugin::player::player_move::PlayerMoveEvent;
 use crate::server::seasonal_events;
-use crate::world::BlockFlags;
+use crate::world::{BlockFlags, World};
 use crate::{
     command::CommandSender,
     entity::player::{ChatMode, Hand, Player},
@@ -36,7 +37,7 @@ use pumpkin_inventory::player::{
 use pumpkin_macros::send_cancellable;
 use pumpkin_protocol::client::play::{
     CBlockUpdate, COpenSignEditor, CPlayerInfoUpdate, CPlayerPosition, CSetContainerSlot,
-    CSetHeldItem, CSystemChatMessage, EquipmentSlot, InitChat, PlayerAction,
+    CSetHeldItem, CSystemChatMessage, CTeleportEntity, EquipmentSlot, InitChat, PlayerAction,
 };
 use pumpkin_protocol::codec::item_stack_seralizer::ItemStackSerializer;
 use pumpkin_protocol::codec::var_int::VarInt;
@@ -214,6 +215,40 @@ impl Player {
         self.set_client_loaded(true);
     }
 
+    /// Returns whether syncing the position was needed
+    async fn sync_position(
+        &self,
+        world: &World,
+        pos: Vector3<f64>,
+        last_pos: Vector3<f64>,
+        yaw: f32,
+        pitch: f32,
+        on_ground: bool,
+    ) -> bool {
+        let delta = Vector3::new(pos.x - last_pos.x, pos.y - last_pos.y, pos.z - last_pos.z);
+        let entity_id = self.entity_id();
+
+        // Teleport when more than 8 blocks (-8..=7.999755859375) (checking 8²)
+        if delta.length_squared() < 64.0 {
+            return false;
+        }
+        // Sync position with all other players.
+        world
+            .broadcast_packet_except(
+                &[self.gameprofile.id],
+                &CTeleportEntity::new(
+                    entity_id.into(),
+                    pos,
+                    Vector3::new(0.0, 0.0, 0.0),
+                    yaw,
+                    pitch,
+                    on_ground,
+                ),
+            )
+            .await;
+        true
+    }
+
     pub async fn handle_position(self: &Arc<Self>, packet: SPlayerPosition) {
         if !self.has_client_loaded() {
             return;
@@ -243,54 +278,38 @@ impl Player {
             };
 
             'after: {
-                let position = event.to;
+                let pos = event.to;
                 let entity = &self.living_entity.entity;
                 let last_pos = entity.pos.load();
-                self.living_entity.set_pos(position);
+                self.living_entity.set_pos(pos);
 
-                let height_difference = position.y - last_pos.y;
-                if entity.on_ground.load(std::sync::atomic::Ordering::Relaxed)
-                    && !packet.ground
-                    && height_difference > 0.0
-                {
+                let height_difference = pos.y - last_pos.y;
+                if entity.on_ground.load(Ordering::Relaxed) && !packet.ground && height_difference > 0.0 {
                     self.jump().await;
                 }
 
-                entity
-                    .on_ground
-                    .store(packet.ground, std::sync::atomic::Ordering::Relaxed);
-
-                let entity_id = entity.entity_id;
-                let Vector3 { x, y, z } = position;
-                let world = &entity.world.read().await;
-
-                // let delta = Vector3::new(x - lastx, y - lasty, z - lastz);
-                // let velocity = self.velocity;
+                entity.on_ground.store(packet.ground, Ordering::Relaxed);
+                let world = &self.world().await;
 
-                // // The player is falling down fast; we should account for that.
-                // let max_speed = if self.fall_flying { 300.0 } else { 100.0 };
-
-                // Teleport when more than 8 blocks (i guess 8 blocks)
-                // TODO: REPLACE * 2.0 by movement packets. See Vanilla for details.
-                // if delta.length_squared() - velocity.length_squared() > max_speed * 2.0 {
-                //     self.teleport(x, y, z, self.entity.yaw, self.entity.pitch);
-                //     return;
-                // }
-                // Send the new position to all other players.
-                world
-                    .broadcast_packet_except(
-                        &[self.gameprofile.id],
-                        &CUpdateEntityPos::new(
-                            entity_id.into(),
-                            Vector3::new(
-                                x.mul_add(4096.0, -(last_pos.x * 4096.0)) as i16,
-                                y.mul_add(4096.0, -(last_pos.y * 4096.0)) as i16,
-                                z.mul_add(4096.0, -(last_pos.z * 4096.0)) as i16,
+                // TODO: Warn when player moves to quickly
+                if !self.sync_position(world, pos, last_pos, entity.yaw.load(), entity.pitch.load(), packet.ground).await {
+                    // Send the new position to all other players.
+                    world
+                        .broadcast_packet_except(
+                            &[self.gameprofile.id],
+                            &CUpdateEntityPos::new(
+                                self.entity_id().into(),
+                                Vector3::new(
+                                    pos.x.mul_add(4096.0, -(last_pos.x * 4096.0)) as i16,
+                                    pos.y.mul_add(4096.0, -(last_pos.y * 4096.0)) as i16,
+                                    pos.z.mul_add(4096.0, -(last_pos.z * 4096.0)) as i16,
+                                ),
+                                packet.ground,
                             ),
-                            packet.ground,
-                        ),
-                    )
-                    .await;
+                        )
+                        .await;
+                }
+
                 if !self.abilities.lock().await.flying {
                     self.living_entity
                         .update_fall_distance(
@@ -302,9 +321,9 @@ impl Player {
                 }
                 chunker::update_position(self).await;
                 self.progress_motion(Vector3::new(
-                    position.x - last_pos.x,
-                    position.y - last_pos.y,
-                    position.z - last_pos.z,
+                    pos.x - last_pos.x,
+                    pos.y - last_pos.y,
+                    pos.z - last_pos.z,
                 ))
                 .await;
             }
@@ -356,12 +375,12 @@ impl Player {
             );
 
             'after: {
-                let position = event.to;
+                let pos = event.to;
                 let entity = &self.living_entity.entity;
                 let last_pos = entity.pos.load();
-                self.living_entity.set_pos(position);
+                self.living_entity.set_pos(pos);
 
-                let height_difference = position.y - last_pos.y;
+                let height_difference = pos.y - last_pos.y;
                 if entity.on_ground.load(std::sync::atomic::Ordering::Relaxed)
                     && !packet.ground
                     && height_difference > 0.0
@@ -375,43 +394,36 @@ impl Player {
                 entity.set_rotation(wrap_degrees(packet.yaw) % 360.0, wrap_degrees(packet.pitch));
 
                 let entity_id = entity.entity_id;
-                let Vector3 { x, y, z } = position;
 
                 let yaw = (entity.yaw.load() * 256.0 / 360.0).rem_euclid(256.0);
                 let pitch = (entity.pitch.load() * 256.0 / 360.0).rem_euclid(256.0);
                 // let head_yaw = (entity.head_yaw * 256.0 / 360.0).floor();
                 let world = &entity.world.read().await;
 
-                // let delta = Vector3::new(x - lastx, y - lasty, z - lastz);
-                // let velocity = self.velocity;
-
-                // // The player is falling down fast; we should account for that.
-                // let max_speed = if self.fall_flying { 300.0 } else { 100.0 };
-
-                // // Teleport when more than 8 blocks (i guess 8 blocks)
-                // // TODO: REPLACE * 2.0 by movement packets. see vanilla for details
-                // if delta.length_squared() - velocity.length_squared() > max_speed * 2.0 {
-                //     self.teleport(x, y, z, yaw, pitch);
-                //     return;
-                // }
-                // Send the new position to all other players.
-
-                world
-                    .broadcast_packet_except(
-                        &[self.gameprofile.id],
-                        &CUpdateEntityPosRot::new(
-                            entity_id.into(),
-                            Vector3::new(
-                                x.mul_add(4096.0, -(last_pos.x * 4096.0)) as i16,
-                                y.mul_add(4096.0, -(last_pos.y * 4096.0)) as i16,
-                                z.mul_add(4096.0, -(last_pos.z * 4096.0)) as i16,
+                // TODO: Warn when player moves to quickly
+                if !self
+                    .sync_position(world, pos, last_pos, yaw, pitch, packet.ground)
+                    .await
+                {
+                    // Send the new position to all other players.
+                    world
+                        .broadcast_packet_except(
+                            &[self.gameprofile.id],
+                            &CUpdateEntityPosRot::new(
+                                entity_id.into(),
+                                Vector3::new(
+                                    pos.x.mul_add(4096.0, -(last_pos.x * 4096.0)) as i16,
+                                    pos.y.mul_add(4096.0, -(last_pos.y * 4096.0)) as i16,
+                                    pos.z.mul_add(4096.0, -(last_pos.z * 4096.0)) as i16,
+                                ),
+                                yaw as u8,
+                                pitch as u8,
+                                packet.ground,
                             ),
-                            yaw as u8,
-                            pitch as u8,
-                            packet.ground,
-                        ),
-                    )
-                    .await;
+                        )
+                        .await;
+                }
+
                 world
                     .broadcast_packet_except(
                         &[self.gameprofile.id],
@@ -429,9 +441,9 @@ impl Player {
                 }
                 chunker::update_position(self).await;
                 self.progress_motion(Vector3::new(
-                    position.x - last_pos.x,
-                    position.y - last_pos.y,
-                    position.z - last_pos.z,
+                    pos.x - last_pos.x,
+                    pos.y - last_pos.y,
+                    pos.z - last_pos.z,
                 ))
                 .await;
             }


Reviews:
user: login: Snowiiii
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-17T17:58:08Z
user: login: Alvsch
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-17T17:59:18Z
user: login: Alvsch
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-17T18:00:25Z

Review comments:
diff_hunk: @@ -0,0 +1,65 @@
+use std::io::Write;
+
+use pumpkin_data::packet::clientbound::PLAY_TELEPORT_ENTITY;
+use pumpkin_macros::packet;
+use pumpkin_util::math::vector3::Vector3;
+
+use crate::{
+    ClientPacket, PositionFlag, VarInt,
+    ser::{NetworkWriteExt, WritingError},
+};
+
+// https://minecraft.wiki/w/Java_Edition_protocol#Synchronize_Vehicle_Position
+// Badly documented and confusing packet imo
+#[packet(PLAY_TELEPORT_ENTITY)]
+pub struct CSynchronizeVehiclePosition<'a> {
path: pumpkin-protocol/src/client/play/synchronize_vehicle_position.rs
user: login: Snowiiii
body: Why is this called `CSynchronizeVehiclePosition` ? also we already have a packet called `CTeleportEntity`
created_at: 2025-04-17T17:58:08Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,65 @@
+use std::io::Write;
+
+use pumpkin_data::packet::clientbound::PLAY_TELEPORT_ENTITY;
+use pumpkin_macros::packet;
+use pumpkin_util::math::vector3::Vector3;
+
+use crate::{
+    ClientPacket, PositionFlag, VarInt,
+    ser::{NetworkWriteExt, WritingError},
+};
+
+// https://minecraft.wiki/w/Java_Edition_protocol#Synchronize_Vehicle_Position
+// Badly documented and confusing packet imo
+#[packet(PLAY_TELEPORT_ENTITY)]
+pub struct CSynchronizeVehiclePosition<'a> {
path: pumpkin-protocol/src/client/play/synchronize_vehicle_position.rs
user: login: Alvsch
body: check out the linked docs, idk everything is very confusing
created_at: 2025-04-17T17:59:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,65 @@
+use std::io::Write;
+
+use pumpkin_data::packet::clientbound::PLAY_TELEPORT_ENTITY;
+use pumpkin_macros::packet;
+use pumpkin_util::math::vector3::Vector3;
+
+use crate::{
+    ClientPacket, PositionFlag, VarInt,
+    ser::{NetworkWriteExt, WritingError},
+};
+
+// https://minecraft.wiki/w/Java_Edition_protocol#Synchronize_Vehicle_Position
+// Badly documented and confusing packet imo
+#[packet(PLAY_TELEPORT_ENTITY)]
+pub struct CSynchronizeVehiclePosition<'a> {
path: pumpkin-protocol/src/client/play/synchronize_vehicle_position.rs
user: login: Alvsch
body: they renamed the packet resources in 1.21 or smth
created_at: 2025-04-17T18:00:24Z
author_association: CONTRIBUTOR

PR comments:
user: login: Snowiiii
created_at: 2025-04-24T12:38:01Z
body: Looks good. Nice to have that finally fixed. Thank you @Alvsch 
author_association: MEMBER
user: login: zly2006
created_at: 2025-04-24T15:52:09Z
body: Hi, I disagree with this PR. I think the packets naming should follow mojang's names, so relatives should be in CTeleportEntity. Accordingly, another packet (the new CTeleportEntity after this PR) should be named CEntityPositionSync.

In fact, the original CTeleportEntity is the packet sent when calling player.teleport in vanilla, CSynchronizeVehiclePosition totally makes no sense.
author_association: CONTRIBUTOR