use crate::{bits::RlBits, network::attributes::Attribute};
use bitter::{BitReader, LittleEndianReader};
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Serialize)]
pub struct Vector3f {
pub x: f32,
pub y: f32,
pub z: f32,
}
impl Vector3f {
#[inline]
pub fn decode(bits: &mut LittleEndianReader<'_>, net_version: i32) -> Option<Vector3f> {
Vector3i::decode(bits, net_version).map(|vec| Vector3f {
x: (vec.x as f32) / 100.0,
y: (vec.y as f32) / 100.0,
z: (vec.z as f32) / 100.0,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
pub struct Vector3i {
pub x: i32,
pub y: i32,
pub z: i32,
}
impl Vector3i {
#[inline]
pub fn decode(bits: &mut LittleEndianReader<'_>, net_version: i32) -> Option<Vector3i> {
if bits.unbuffered_bytes_remaining() >= 16 {
bits.refill_lookahead();
let size_bits = bits.peek_bits_max_computed(4, if net_version >= 7 { 22 } else { 20 });
let bias = 1 << (size_bits + 1);
let bit_limit = (size_bits + 2) as u32;
let dx = bits.peek_and_consume(bit_limit) as u32;
bits.refill_lookahead();
let dy = bits.peek_and_consume(bit_limit) as u32;
let dz = bits.peek_and_consume(bit_limit) as u32;
Some(Vector3i {
x: (dx as i32) - bias,
y: (dy as i32) - bias,
z: (dz as i32) - bias,
})
} else {
Vector3i::eof_decode(bits, net_version)
}
}
#[cold]
pub fn eof_decode(bits: &mut LittleEndianReader<'_>, net_version: i32) -> Option<Vector3i> {
bits.refill_lookahead();
if bits.lookahead_bits() < 5 {
return None;
}
let size_bits = bits.peek_bits_max_computed(4, if net_version >= 7 { 22 } else { 20 });
let bias = 1 << (size_bits + 1);
let bit_limit = (size_bits + 2) as u32;
if !bits.has_bits_remaining(3 * bit_limit as usize) {
return None;
}
let dx = bits.peek_and_consume(bit_limit) as u32;
bits.refill_lookahead();
debug_assert!(bits.lookahead_bits() >= bit_limit * 2);
let dy = bits.peek_and_consume(bit_limit) as u32;
let dz = bits.peek_and_consume(bit_limit) as u32;
Some(Vector3i {
x: (dx as i32) - bias,
y: (dy as i32) - bias,
z: (dz as i32) - bias,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize)]
pub struct Quaternion {
pub x: f32,
pub y: f32,
pub z: f32,
pub w: f32,
}
impl Quaternion {
#[inline]
fn unpack(val: u32) -> f32 {
let max_value = (1 << 18) - 1;
let pos_range = (val as f32) / (max_value as f32);
let range = (pos_range - 0.5) * 2.0;
range * std::f32::consts::FRAC_1_SQRT_2
}
#[inline]
fn compressed_f32(bits: &mut LittleEndianReader<'_>) -> f32 {
let res = bits.peek_and_consume(16) as i32;
((res + i32::from(i16::MIN)) as f32) * (i16::MAX as f32).recip()
}
pub fn decode_compressed(bits: &mut LittleEndianReader<'_>) -> Option<Self> {
bits.refill_lookahead();
if bits.lookahead_bits() >= 3 * 16 {
let x = Quaternion::compressed_f32(bits);
let y = Quaternion::compressed_f32(bits);
let z = Quaternion::compressed_f32(bits);
Some(Quaternion { x, y, z, w: 0.0 })
} else {
None
}
}
pub fn decode(bits: &mut LittleEndianReader<'_>) -> Option<Self> {
bits.refill_lookahead();
if bits.lookahead_bits() < 2 + 3 * 18 {
return None;
}
let largest = bits.peek_and_consume(2) as u32;
let a = Quaternion::unpack(bits.peek_and_consume(18) as u32);
let b = Quaternion::unpack(bits.peek_and_consume(18) as u32);
let c = Quaternion::unpack(bits.peek_and_consume(18) as u32);
let extra = (c.mul_add(-c, b.mul_add(-b, a.mul_add(-a, 1.0)))).sqrt();
match largest {
0 => Some(Quaternion {
x: extra,
y: a,
z: b,
w: c,
}),
1 => Some(Quaternion {
x: a,
y: extra,
z: b,
w: c,
}),
2 => Some(Quaternion {
x: a,
y: b,
z: extra,
w: c,
}),
3 => Some(Quaternion {
x: a,
y: b,
z: c,
w: extra,
}),
_ => unreachable!(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
pub struct Rotation {
pub yaw: Option<i8>,
pub pitch: Option<i8>,
pub roll: Option<i8>,
}
impl Rotation {
pub fn decode(bits: &mut LittleEndianReader<'_>) -> Option<Rotation> {
bits.refill_lookahead();
if bits.lookahead_bits() >= 3 * 9 {
let has_yaw = bits.peek_and_consume(1);
let yaw = bits.peek_and_consume((has_yaw << 3) as u32) as i8;
let has_pitch = bits.peek_and_consume(1);
let pitch = bits.peek_and_consume((has_pitch << 3) as u32) as i8;
let has_roll = bits.peek_and_consume(1);
let roll = bits.peek_and_consume((has_roll << 3) as u32) as i8;
Some(Rotation {
yaw: if has_yaw != 0 { Some(yaw) } else { None },
pitch: if has_pitch != 0 { Some(pitch) } else { None },
roll: if has_roll != 0 { Some(roll) } else { None },
})
} else {
let yaw = bits.if_get(LittleEndianReader::read_i8)?;
let pitch = bits.if_get(LittleEndianReader::read_i8)?;
let roll = bits.if_get(LittleEndianReader::read_i8)?;
Some(Rotation { yaw, pitch, roll })
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SpawnTrajectory {
None,
Location,
LocationAndRotation,
}
#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct UpdatedAttribute {
pub actor_id: ActorId,
pub stream_id: StreamId,
pub object_id: ObjectId,
pub attribute: Attribute,
}
#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct Frame {
pub time: f32,
pub delta: f32,
pub new_actors: Vec<NewActor>,
pub deleted_actors: Vec<ActorId>,
pub updated_actors: Vec<UpdatedAttribute>,
}
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash, Serialize, Default)]
pub struct ObjectId(pub i32);
impl From<ObjectId> for i32 {
fn from(x: ObjectId) -> i32 {
x.0
}
}
impl From<ObjectId> for usize {
fn from(x: ObjectId) -> usize {
x.0 as usize
}
}
impl fmt::Display for ObjectId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash, Serialize)]
pub struct StreamId(pub i32);
impl From<StreamId> for i32 {
fn from(x: StreamId) -> i32 {
x.0
}
}
impl From<StreamId> for usize {
fn from(val: StreamId) -> Self {
val.0 as usize
}
}
impl fmt::Display for StreamId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash, Serialize)]
pub struct ActorId(pub i32);
impl From<ActorId> for i32 {
fn from(x: ActorId) -> i32 {
x.0
}
}
impl From<ActorId> for usize {
fn from(val: ActorId) -> Self {
val.0 as usize
}
}
impl fmt::Display for ActorId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
pub struct NewActor {
pub actor_id: ActorId,
pub name_id: Option<i32>,
pub object_id: ObjectId,
pub initial_trajectory: Trajectory,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
pub struct Trajectory {
pub location: Option<Vector3i>,
pub rotation: Option<Rotation>,
}
impl Trajectory {
pub fn from_spawn(
bits: &mut LittleEndianReader<'_>,
sp: SpawnTrajectory,
net_version: i32,
) -> Option<Trajectory> {
match sp {
SpawnTrajectory::None => Some(Trajectory {
location: None,
rotation: None,
}),
SpawnTrajectory::Location => Vector3i::decode(bits, net_version).map(|v| Trajectory {
location: Some(v),
rotation: None,
}),
SpawnTrajectory::LocationAndRotation => {
let v = Vector3i::decode(bits, net_version)?;
let r = Rotation::decode(bits)?;
Some(Trajectory {
location: Some(v),
rotation: Some(r),
})
}
}
}
}
pub(crate) fn normalize_object(name: &str) -> &str {
const PREFIX: &str = "TheWorld:PersistentLevel.";
if name.len() <= "TheWorld:PersistentLevel.CrowdActor_TA".len() {
return name;
}
let Some(rest) = name.strip_prefix(PREFIX).or_else(|| {
name.split_once('.')
.and_then(|(_, suffix)| suffix.strip_prefix(PREFIX))
}) else {
return name;
};
if rest.starts_with("CrowdActor_TA") {
"TheWorld:PersistentLevel.CrowdActor_TA"
} else if rest.starts_with("CrowdManager_TA") {
"TheWorld:PersistentLevel.CrowdManager_TA"
} else if rest.starts_with("VehiclePickup_Boost_TA") {
"TheWorld:PersistentLevel.VehiclePickup_Boost_TA"
} else if rest.starts_with("InMapScoreboard_TA") {
"TheWorld:PersistentLevel.InMapScoreboard_TA"
} else if rest.starts_with("BreakOutActor_Platform_TA") {
"TheWorld:PersistentLevel.BreakOutActor_Platform_TA"
} else if rest.starts_with("PlayerStart_Platform_TA") {
"TheWorld:PersistentLevel.PlayerStart_Platform_TA"
} else {
name
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_decode_vector() {
let mut bitter =
LittleEndianReader::new(&[0b0000_0110, 0b0000_1000, 0b1101_1000, 0b0000_1101]);
let v = Vector3i::decode(&mut bitter, 5).unwrap();
assert_eq!(v, Vector3i { x: 0, y: 0, z: 93 });
}
#[test]
fn test_decode_rotation() {
let mut bitter = LittleEndianReader::new(&[0b0000_0101, 0b0000_0000]);
let v = Rotation::decode(&mut bitter).unwrap();
assert_eq!(
v,
Rotation {
yaw: Some(2),
pitch: None,
roll: None,
}
);
}
}