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
This commit is contained in:
parent
72d73f529f
commit
0034756339
|
@ -597,6 +597,90 @@ impl Lengthable for i32 {
|
|||
}
|
||||
}
|
||||
|
||||
use num_traits::cast::{cast, NumCast};
|
||||
/// `FixedPoint5` has the 5 least-significant bits for the fractional
|
||||
/// part, upper for integer part: https://wiki.vg/Data_types#Fixed-point_numbers
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct FixedPoint5<T>(T);
|
||||
|
||||
impl<T: Serializable> Serializable for FixedPoint5<T> {
|
||||
fn read_from<R: io::Read>(buf: &mut R) -> Result<Self, Error> {
|
||||
Ok(Self(Serializable::read_from(buf)?))
|
||||
}
|
||||
|
||||
fn write_to<W: io::Write>(&self, buf: &mut W) -> Result<(), Error> {
|
||||
self.0.write_to(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: default::Default> default::Default for FixedPoint5<T> {
|
||||
fn default() -> Self {
|
||||
Self(T::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: NumCast> convert::From<f64> for FixedPoint5<T> {
|
||||
fn from(x: f64) -> Self {
|
||||
let n: T = cast(x * 32.0).unwrap();
|
||||
FixedPoint5::<T>(n)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: NumCast> convert::From<FixedPoint5<T>> for f64 {
|
||||
fn from(x: FixedPoint5<T>) -> Self {
|
||||
let f: f64 = cast(x.0).unwrap();
|
||||
f / 32.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Debug for FixedPoint5<T> where T: fmt::Display, f64: convert::From<T>, T: NumCast + Copy {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let x: f64 = (*self).into();
|
||||
write!(f, "FixedPoint5(#{} = {}f)", self.0, x)
|
||||
}
|
||||
}
|
||||
|
||||
/// `FixedPoint12` is like `FixedPoint5` but the fractional part is 12-bit
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct FixedPoint12<T>(T);
|
||||
|
||||
impl<T: Serializable> Serializable for FixedPoint12<T> {
|
||||
fn read_from<R: io::Read>(buf: &mut R) -> Result<Self, Error> {
|
||||
Ok(Self(Serializable::read_from(buf)?))
|
||||
}
|
||||
|
||||
fn write_to<W: io::Write>(&self, buf: &mut W) -> Result<(), Error> {
|
||||
self.0.write_to(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: default::Default> default::Default for FixedPoint12<T> {
|
||||
fn default() -> Self {
|
||||
Self(T::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: NumCast> convert::From<f64> for FixedPoint12<T> {
|
||||
fn from(x: f64) -> Self {
|
||||
let n: T = cast(x * 32.0 * 128.0).unwrap();
|
||||
FixedPoint12::<T>(n)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: NumCast> convert::From<FixedPoint12<T>> for f64 {
|
||||
fn from(x: FixedPoint12<T>) -> Self {
|
||||
let f: f64 = cast(x.0).unwrap();
|
||||
f / (32.0 * 128.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Debug for FixedPoint12<T> where T: fmt::Display, f64: convert::From<T>, T: NumCast + Copy {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let x: f64 = (*self).into();
|
||||
write!(f, "FixedPoint12(#{} = {}f)", self.0, x)
|
||||
}
|
||||
}
|
||||
|
||||
/// `VarInt` have a variable size (between 1 and 5 bytes) when encoded based
|
||||
/// on the size of the number
|
||||
#[derive(Clone, Copy)]
|
||||
|
|
|
@ -495,9 +495,9 @@ state_packets!(
|
|||
field entity_id: VarInt =,
|
||||
field uuid: UUID =,
|
||||
field ty: u8 =,
|
||||
field x: i32 =,
|
||||
field y: i32 =,
|
||||
field z: i32 =,
|
||||
field x: FixedPoint5<i32> =,
|
||||
field y: FixedPoint5<i32> =,
|
||||
field z: FixedPoint5<i32> =,
|
||||
field pitch: i8 =,
|
||||
field yaw: i8 =,
|
||||
field data: i32 =,
|
||||
|
@ -508,9 +508,9 @@ state_packets!(
|
|||
packet SpawnObject_i32_NoUUID {
|
||||
field entity_id: VarInt =,
|
||||
field ty: u8 =,
|
||||
field x: i32 =,
|
||||
field y: i32 =,
|
||||
field z: i32 =,
|
||||
field x: FixedPoint5<i32> =,
|
||||
field y: FixedPoint5<i32> =,
|
||||
field z: FixedPoint5<i32> =,
|
||||
field pitch: i8 =,
|
||||
field yaw: i8 =,
|
||||
field data: i32 =,
|
||||
|
@ -530,9 +530,9 @@ state_packets!(
|
|||
}
|
||||
packet SpawnExperienceOrb_i32 {
|
||||
field entity_id: VarInt =,
|
||||
field x: i32 =,
|
||||
field y: i32 =,
|
||||
field z: i32 =,
|
||||
field x: FixedPoint5<i32> =,
|
||||
field y: FixedPoint5<i32> =,
|
||||
field z: FixedPoint5<i32> =,
|
||||
field count: i16 =,
|
||||
}
|
||||
/// SpawnGlobalEntity spawns an entity which is visible from anywhere in the
|
||||
|
@ -547,9 +547,9 @@ state_packets!(
|
|||
packet SpawnGlobalEntity_i32 {
|
||||
field entity_id: VarInt =,
|
||||
field ty: u8 =,
|
||||
field x: i32 =,
|
||||
field y: i32 =,
|
||||
field z: i32 =,
|
||||
field x: FixedPoint5<i32> =,
|
||||
field y: FixedPoint5<i32> =,
|
||||
field z: FixedPoint5<i32> =,
|
||||
}
|
||||
/// SpawnMob is used to spawn a living entity into the world when it is in
|
||||
/// range of the client.
|
||||
|
@ -587,9 +587,9 @@ state_packets!(
|
|||
field entity_id: VarInt =,
|
||||
field uuid: UUID =,
|
||||
field ty: u8 =,
|
||||
field x: i32 =,
|
||||
field y: i32 =,
|
||||
field z: i32 =,
|
||||
field x: FixedPoint5<i32> =,
|
||||
field y: FixedPoint5<i32> =,
|
||||
field z: FixedPoint5<i32> =,
|
||||
field yaw: i8 =,
|
||||
field pitch: i8 =,
|
||||
field head_pitch: i8 =,
|
||||
|
@ -601,9 +601,9 @@ state_packets!(
|
|||
packet SpawnMob_u8_i32_NoUUID {
|
||||
field entity_id: VarInt =,
|
||||
field ty: u8 =,
|
||||
field x: i32 =,
|
||||
field y: i32 =,
|
||||
field z: i32 =,
|
||||
field x: FixedPoint5<i32> =,
|
||||
field y: FixedPoint5<i32> =,
|
||||
field z: FixedPoint5<i32> =,
|
||||
field yaw: i8 =,
|
||||
field pitch: i8 =,
|
||||
field head_pitch: i8 =,
|
||||
|
@ -651,9 +651,9 @@ state_packets!(
|
|||
packet SpawnPlayer_i32 {
|
||||
field entity_id: VarInt =,
|
||||
field uuid: UUID =,
|
||||
field x: i32 =,
|
||||
field y: i32 =,
|
||||
field z: i32 =,
|
||||
field x: FixedPoint5<i32> =,
|
||||
field y: FixedPoint5<i32> =,
|
||||
field z: FixedPoint5<i32> =,
|
||||
field yaw: i8 =,
|
||||
field pitch: i8 =,
|
||||
field metadata: types::Metadata =,
|
||||
|
@ -661,9 +661,9 @@ state_packets!(
|
|||
packet SpawnPlayer_i32_HeldItem {
|
||||
field entity_id: VarInt =,
|
||||
field uuid: UUID =,
|
||||
field x: i32 =,
|
||||
field y: i32 =,
|
||||
field z: i32 =,
|
||||
field x: FixedPoint5<i32> =,
|
||||
field y: FixedPoint5<i32> =,
|
||||
field z: FixedPoint5<i32> =,
|
||||
field yaw: i8 =,
|
||||
field pitch: i8 =,
|
||||
field current_item: u16 =,
|
||||
|
@ -674,9 +674,9 @@ state_packets!(
|
|||
field uuid: String =,
|
||||
field name: String =,
|
||||
field properties: LenPrefixed<VarInt, packet::SpawnProperty> =,
|
||||
field x: i32 =,
|
||||
field y: i32 =,
|
||||
field z: i32 =,
|
||||
field x: FixedPoint5<i32> =,
|
||||
field y: FixedPoint5<i32> =,
|
||||
field z: FixedPoint5<i32> =,
|
||||
field yaw: i8 =,
|
||||
field pitch: i8 =,
|
||||
field current_item: u16 =,
|
||||
|
@ -1159,48 +1159,48 @@ state_packets!(
|
|||
/// EntityMove moves the entity with the id by the offsets provided.
|
||||
packet EntityMove_i16 {
|
||||
field entity_id: VarInt =,
|
||||
field delta_x: i16 =,
|
||||
field delta_y: i16 =,
|
||||
field delta_z: i16 =,
|
||||
field delta_x: FixedPoint12<i16> =,
|
||||
field delta_y: FixedPoint12<i16> =,
|
||||
field delta_z: FixedPoint12<i16> =,
|
||||
field on_ground: bool =,
|
||||
}
|
||||
packet EntityMove_i8 {
|
||||
field entity_id: VarInt =,
|
||||
field delta_x: i8 =,
|
||||
field delta_y: i8 =,
|
||||
field delta_z: i8 =,
|
||||
field delta_x: FixedPoint5<i8> =,
|
||||
field delta_y: FixedPoint5<i8> =,
|
||||
field delta_z: FixedPoint5<i8> =,
|
||||
field on_ground: bool =,
|
||||
}
|
||||
packet EntityMove_i8_i32_NoGround {
|
||||
field entity_id: i32 =,
|
||||
field delta_x: i8 =,
|
||||
field delta_y: i8 =,
|
||||
field delta_z: i8 =,
|
||||
field delta_x: FixedPoint5<i8> =,
|
||||
field delta_y: FixedPoint5<i8> =,
|
||||
field delta_z: FixedPoint5<i8> =,
|
||||
}
|
||||
/// EntityLookAndMove is a combination of EntityMove and EntityLook.
|
||||
packet EntityLookAndMove_i16 {
|
||||
field entity_id: VarInt =,
|
||||
field delta_x: i16 =,
|
||||
field delta_y: i16 =,
|
||||
field delta_z: i16 =,
|
||||
field delta_x: FixedPoint12<i16> =,
|
||||
field delta_y: FixedPoint12<i16> =,
|
||||
field delta_z: FixedPoint12<i16> =,
|
||||
field yaw: i8 =,
|
||||
field pitch: i8 =,
|
||||
field on_ground: bool =,
|
||||
}
|
||||
packet EntityLookAndMove_i8 {
|
||||
field entity_id: VarInt =,
|
||||
field delta_x: i8 =,
|
||||
field delta_y: i8 =,
|
||||
field delta_z: i8 =,
|
||||
field delta_x: FixedPoint5<i8> =,
|
||||
field delta_y: FixedPoint5<i8> =,
|
||||
field delta_z: FixedPoint5<i8> =,
|
||||
field yaw: i8 =,
|
||||
field pitch: i8 =,
|
||||
field on_ground: bool =,
|
||||
}
|
||||
packet EntityLookAndMove_i8_i32_NoGround {
|
||||
field entity_id: i32 =,
|
||||
field delta_x: i8 =,
|
||||
field delta_y: i8 =,
|
||||
field delta_z: i8 =,
|
||||
field delta_x: FixedPoint5<i8> =,
|
||||
field delta_y: FixedPoint5<i8> =,
|
||||
field delta_z: FixedPoint5<i8> =,
|
||||
field yaw: i8 =,
|
||||
field pitch: i8 =,
|
||||
}
|
||||
|
@ -1677,18 +1677,18 @@ state_packets!(
|
|||
}
|
||||
packet EntityTeleport_i32 {
|
||||
field entity_id: VarInt =,
|
||||
field x: i32 =,
|
||||
field y: i32 =,
|
||||
field z: i32 =,
|
||||
field x: FixedPoint5<i32> =,
|
||||
field y: FixedPoint5<i32> =,
|
||||
field z: FixedPoint5<i32> =,
|
||||
field yaw: i8 =,
|
||||
field pitch: i8 =,
|
||||
field on_ground: bool =,
|
||||
}
|
||||
packet EntityTeleport_i32_i32_NoGround {
|
||||
field entity_id: i32 =,
|
||||
field x: i32 =,
|
||||
field y: i32 =,
|
||||
field z: i32 =,
|
||||
field x: FixedPoint5<i32> =,
|
||||
field y: FixedPoint5<i32> =,
|
||||
field z: FixedPoint5<i32> =,
|
||||
field yaw: i8 =,
|
||||
field pitch: i8 =,
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue