Skip to content

Commit 72dbf80

Browse files
committed
Fix movement fixed-point packets, misplaced players on 1.7/8 (#140)
Movement packets were handled incorrectly, because although the fields are specified as integers they are actually fixed-point values, which need to be converted to floating-point before use. These fields were converted with `as f64`, but they actually need to be scaled. To fix this add several new types, FixedPoint5 for 5-bit fractional fixed-point and FixedPoint12 for 12-bit. Both are parameterized by an integer type: FixedPoint5<i32> and FixedPoint5<i8> for 1.7.10/1.8.9, FixedPoint12<i16> for 1.9+. This moves the calculation into the packet field parsing, so it no longer has to be calculated in src/server/mod.rs since the scaling is taken care of as part of the field type. This fixes the long-standing invisible or actually misplaced players bug on 1.7.10 and 1.8.9, closes #139. * Add new FixedPoint5<T> type for 1.7/8, https://wiki.vg/Data_types#Fixed-point_numbers * Add FixedPoint12<i16> for 1.9+, moving type conversion into packet type https://wiki.vg/index.php?title=Protocol#Entity_Relative_Move * Add num-traits 0.2.6 dependency for NumCast to use instead of From * Use FixedPoint5<i32> in spawn object, experience orb, global entity, mob, player, teleport * Use FixedPoint5<i8> and FixedPoint12<i16> in entity move, look and move * Update packet handling bouncer functions, using f64::from for each conversion
1 parent c97f57b commit 72dbf80

File tree

2 files changed

+135
-51
lines changed

2 files changed

+135
-51
lines changed

protocol/src/mod.rs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,90 @@ impl Lengthable for i32 {
597597
}
598598
}
599599

600+
use num_traits::cast::{cast, NumCast};
601+
/// `FixedPoint5` has the 5 least-significant bits for the fractional
602+
/// part, upper for integer part: https://wiki.vg/Data_types#Fixed-point_numbers
603+
#[derive(Clone, Copy)]
604+
pub struct FixedPoint5<T>(T);
605+
606+
impl<T: Serializable> Serializable for FixedPoint5<T> {
607+
fn read_from<R: io::Read>(buf: &mut R) -> Result<Self, Error> {
608+
Ok(Self(Serializable::read_from(buf)?))
609+
}
610+
611+
fn write_to<W: io::Write>(&self, buf: &mut W) -> Result<(), Error> {
612+
self.0.write_to(buf)
613+
}
614+
}
615+
616+
impl<T: default::Default> default::Default for FixedPoint5<T> {
617+
fn default() -> Self {
618+
Self(T::default())
619+
}
620+
}
621+
622+
impl<T: NumCast> convert::From<f64> for FixedPoint5<T> {
623+
fn from(x: f64) -> Self {
624+
let n: T = cast(x * 32.0).unwrap();
625+
FixedPoint5::<T>(n)
626+
}
627+
}
628+
629+
impl<T: NumCast> convert::From<FixedPoint5<T>> for f64 {
630+
fn from(x: FixedPoint5<T>) -> Self {
631+
let f: f64 = cast(x.0).unwrap();
632+
f / 32.0
633+
}
634+
}
635+
636+
impl<T> fmt::Debug for FixedPoint5<T> where T: fmt::Display, f64: convert::From<T>, T: NumCast + Copy {
637+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
638+
let x: f64 = (*self).into();
639+
write!(f, "FixedPoint5(#{} = {}f)", self.0, x)
640+
}
641+
}
642+
643+
/// `FixedPoint12` is like `FixedPoint5` but the fractional part is 12-bit
644+
#[derive(Clone, Copy)]
645+
pub struct FixedPoint12<T>(T);
646+
647+
impl<T: Serializable> Serializable for FixedPoint12<T> {
648+
fn read_from<R: io::Read>(buf: &mut R) -> Result<Self, Error> {
649+
Ok(Self(Serializable::read_from(buf)?))
650+
}
651+
652+
fn write_to<W: io::Write>(&self, buf: &mut W) -> Result<(), Error> {
653+
self.0.write_to(buf)
654+
}
655+
}
656+
657+
impl<T: default::Default> default::Default for FixedPoint12<T> {
658+
fn default() -> Self {
659+
Self(T::default())
660+
}
661+
}
662+
663+
impl<T: NumCast> convert::From<f64> for FixedPoint12<T> {
664+
fn from(x: f64) -> Self {
665+
let n: T = cast(x * 32.0 * 128.0).unwrap();
666+
FixedPoint12::<T>(n)
667+
}
668+
}
669+
670+
impl<T: NumCast> convert::From<FixedPoint12<T>> for f64 {
671+
fn from(x: FixedPoint12<T>) -> Self {
672+
let f: f64 = cast(x.0).unwrap();
673+
f / (32.0 * 128.0)
674+
}
675+
}
676+
677+
impl<T> fmt::Debug for FixedPoint12<T> where T: fmt::Display, f64: convert::From<T>, T: NumCast + Copy {
678+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
679+
let x: f64 = (*self).into();
680+
write!(f, "FixedPoint12(#{} = {}f)", self.0, x)
681+
}
682+
}
683+
600684
/// `VarInt` have a variable size (between 1 and 5 bytes) when encoded based
601685
/// on the size of the number
602686
#[derive(Clone, Copy)]

protocol/src/packet.rs

Lines changed: 51 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -495,9 +495,9 @@ state_packets!(
495495
field entity_id: VarInt =,
496496
field uuid: UUID =,
497497
field ty: u8 =,
498-
field x: i32 =,
499-
field y: i32 =,
500-
field z: i32 =,
498+
field x: FixedPoint5<i32> =,
499+
field y: FixedPoint5<i32> =,
500+
field z: FixedPoint5<i32> =,
501501
field pitch: i8 =,
502502
field yaw: i8 =,
503503
field data: i32 =,
@@ -508,9 +508,9 @@ state_packets!(
508508
packet SpawnObject_i32_NoUUID {
509509
field entity_id: VarInt =,
510510
field ty: u8 =,
511-
field x: i32 =,
512-
field y: i32 =,
513-
field z: i32 =,
511+
field x: FixedPoint5<i32> =,
512+
field y: FixedPoint5<i32> =,
513+
field z: FixedPoint5<i32> =,
514514
field pitch: i8 =,
515515
field yaw: i8 =,
516516
field data: i32 =,
@@ -530,9 +530,9 @@ state_packets!(
530530
}
531531
packet SpawnExperienceOrb_i32 {
532532
field entity_id: VarInt =,
533-
field x: i32 =,
534-
field y: i32 =,
535-
field z: i32 =,
533+
field x: FixedPoint5<i32> =,
534+
field y: FixedPoint5<i32> =,
535+
field z: FixedPoint5<i32> =,
536536
field count: i16 =,
537537
}
538538
/// SpawnGlobalEntity spawns an entity which is visible from anywhere in the
@@ -547,9 +547,9 @@ state_packets!(
547547
packet SpawnGlobalEntity_i32 {
548548
field entity_id: VarInt =,
549549
field ty: u8 =,
550-
field x: i32 =,
551-
field y: i32 =,
552-
field z: i32 =,
550+
field x: FixedPoint5<i32> =,
551+
field y: FixedPoint5<i32> =,
552+
field z: FixedPoint5<i32> =,
553553
}
554554
/// SpawnMob is used to spawn a living entity into the world when it is in
555555
/// range of the client.
@@ -587,9 +587,9 @@ state_packets!(
587587
field entity_id: VarInt =,
588588
field uuid: UUID =,
589589
field ty: u8 =,
590-
field x: i32 =,
591-
field y: i32 =,
592-
field z: i32 =,
590+
field x: FixedPoint5<i32> =,
591+
field y: FixedPoint5<i32> =,
592+
field z: FixedPoint5<i32> =,
593593
field yaw: i8 =,
594594
field pitch: i8 =,
595595
field head_pitch: i8 =,
@@ -601,9 +601,9 @@ state_packets!(
601601
packet SpawnMob_u8_i32_NoUUID {
602602
field entity_id: VarInt =,
603603
field ty: u8 =,
604-
field x: i32 =,
605-
field y: i32 =,
606-
field z: i32 =,
604+
field x: FixedPoint5<i32> =,
605+
field y: FixedPoint5<i32> =,
606+
field z: FixedPoint5<i32> =,
607607
field yaw: i8 =,
608608
field pitch: i8 =,
609609
field head_pitch: i8 =,
@@ -651,19 +651,19 @@ state_packets!(
651651
packet SpawnPlayer_i32 {
652652
field entity_id: VarInt =,
653653
field uuid: UUID =,
654-
field x: i32 =,
655-
field y: i32 =,
656-
field z: i32 =,
654+
field x: FixedPoint5<i32> =,
655+
field y: FixedPoint5<i32> =,
656+
field z: FixedPoint5<i32> =,
657657
field yaw: i8 =,
658658
field pitch: i8 =,
659659
field metadata: types::Metadata =,
660660
}
661661
packet SpawnPlayer_i32_HeldItem {
662662
field entity_id: VarInt =,
663663
field uuid: UUID =,
664-
field x: i32 =,
665-
field y: i32 =,
666-
field z: i32 =,
664+
field x: FixedPoint5<i32> =,
665+
field y: FixedPoint5<i32> =,
666+
field z: FixedPoint5<i32> =,
667667
field yaw: i8 =,
668668
field pitch: i8 =,
669669
field current_item: u16 =,
@@ -674,9 +674,9 @@ state_packets!(
674674
field uuid: String =,
675675
field name: String =,
676676
field properties: LenPrefixed<VarInt, packet::SpawnProperty> =,
677-
field x: i32 =,
678-
field y: i32 =,
679-
field z: i32 =,
677+
field x: FixedPoint5<i32> =,
678+
field y: FixedPoint5<i32> =,
679+
field z: FixedPoint5<i32> =,
680680
field yaw: i8 =,
681681
field pitch: i8 =,
682682
field current_item: u16 =,
@@ -1159,48 +1159,48 @@ state_packets!(
11591159
/// EntityMove moves the entity with the id by the offsets provided.
11601160
packet EntityMove_i16 {
11611161
field entity_id: VarInt =,
1162-
field delta_x: i16 =,
1163-
field delta_y: i16 =,
1164-
field delta_z: i16 =,
1162+
field delta_x: FixedPoint12<i16> =,
1163+
field delta_y: FixedPoint12<i16> =,
1164+
field delta_z: FixedPoint12<i16> =,
11651165
field on_ground: bool =,
11661166
}
11671167
packet EntityMove_i8 {
11681168
field entity_id: VarInt =,
1169-
field delta_x: i8 =,
1170-
field delta_y: i8 =,
1171-
field delta_z: i8 =,
1169+
field delta_x: FixedPoint5<i8> =,
1170+
field delta_y: FixedPoint5<i8> =,
1171+
field delta_z: FixedPoint5<i8> =,
11721172
field on_ground: bool =,
11731173
}
11741174
packet EntityMove_i8_i32_NoGround {
11751175
field entity_id: i32 =,
1176-
field delta_x: i8 =,
1177-
field delta_y: i8 =,
1178-
field delta_z: i8 =,
1176+
field delta_x: FixedPoint5<i8> =,
1177+
field delta_y: FixedPoint5<i8> =,
1178+
field delta_z: FixedPoint5<i8> =,
11791179
}
11801180
/// EntityLookAndMove is a combination of EntityMove and EntityLook.
11811181
packet EntityLookAndMove_i16 {
11821182
field entity_id: VarInt =,
1183-
field delta_x: i16 =,
1184-
field delta_y: i16 =,
1185-
field delta_z: i16 =,
1183+
field delta_x: FixedPoint12<i16> =,
1184+
field delta_y: FixedPoint12<i16> =,
1185+
field delta_z: FixedPoint12<i16> =,
11861186
field yaw: i8 =,
11871187
field pitch: i8 =,
11881188
field on_ground: bool =,
11891189
}
11901190
packet EntityLookAndMove_i8 {
11911191
field entity_id: VarInt =,
1192-
field delta_x: i8 =,
1193-
field delta_y: i8 =,
1194-
field delta_z: i8 =,
1192+
field delta_x: FixedPoint5<i8> =,
1193+
field delta_y: FixedPoint5<i8> =,
1194+
field delta_z: FixedPoint5<i8> =,
11951195
field yaw: i8 =,
11961196
field pitch: i8 =,
11971197
field on_ground: bool =,
11981198
}
11991199
packet EntityLookAndMove_i8_i32_NoGround {
12001200
field entity_id: i32 =,
1201-
field delta_x: i8 =,
1202-
field delta_y: i8 =,
1203-
field delta_z: i8 =,
1201+
field delta_x: FixedPoint5<i8> =,
1202+
field delta_y: FixedPoint5<i8> =,
1203+
field delta_z: FixedPoint5<i8> =,
12041204
field yaw: i8 =,
12051205
field pitch: i8 =,
12061206
}
@@ -1677,18 +1677,18 @@ state_packets!(
16771677
}
16781678
packet EntityTeleport_i32 {
16791679
field entity_id: VarInt =,
1680-
field x: i32 =,
1681-
field y: i32 =,
1682-
field z: i32 =,
1680+
field x: FixedPoint5<i32> =,
1681+
field y: FixedPoint5<i32> =,
1682+
field z: FixedPoint5<i32> =,
16831683
field yaw: i8 =,
16841684
field pitch: i8 =,
16851685
field on_ground: bool =,
16861686
}
16871687
packet EntityTeleport_i32_i32_NoGround {
16881688
field entity_id: i32 =,
1689-
field x: i32 =,
1690-
field y: i32 =,
1691-
field z: i32 =,
1689+
field x: FixedPoint5<i32> =,
1690+
field y: FixedPoint5<i32> =,
1691+
field z: FixedPoint5<i32> =,
16921692
field yaw: i8 =,
16931693
field pitch: i8 =,
16941694
}

0 commit comments

Comments
 (0)