From 009fb123c20e9977da61a325f3eb4b00d3a9cf83 Mon Sep 17 00:00:00 2001 From: Andrei Benea Date: Sun, 25 Feb 2024 17:14:40 +0100 Subject: [PATCH] Make Entity a trait to allow cs2-demo users to define their own Entity structs. --- cs2-demo/src/entity.rs | 256 +++++-------------- cs2-demo/src/entity/decoder.rs | 382 +++++++++++++++-------------- cs2-demo/src/entity/path_name.rs | 41 ++++ cs2-demo/src/entity/property.rs | 162 +++++++++++- cs2-demo/src/entity/send_tables.rs | 96 +++----- cs2-demo/src/visit.rs | 27 +- csdemoparser/src/cs2.rs | 6 +- 7 files changed, 513 insertions(+), 457 deletions(-) create mode 100644 cs2-demo/src/entity/path_name.rs diff --git a/cs2-demo/src/entity.rs b/cs2-demo/src/entity.rs index 60f4737..253ceb0 100644 --- a/cs2-demo/src/entity.rs +++ b/cs2-demo/src/entity.rs @@ -2,15 +2,16 @@ mod class; mod decoder; mod fieldpath; mod property; +mod path_name; mod send_tables; -use std::fmt; use std::rc::Rc; use bitstream_io::BitRead; use tracing::{enabled, trace, Level}; use self::fieldpath::FieldPath; +use self::path_name::PathName; use self::send_tables::{Field, Serializer}; use crate::proto::netmessages::CSVCMsg_PacketEntities; use crate::read::ValveBitReader; @@ -18,17 +19,33 @@ use crate::BitReader; use crate::{Error, Result}; pub use self::class::Classes; -pub use self::property::{Object, Property}; +pub use self::property::{Property, TreeEntity}; pub use self::send_tables::SendTables; -#[derive(Default)] -pub struct Entities { - entities: Vec>, +pub trait Entity: std::fmt::Display { + fn serializer(&self) -> &Rc; + fn get_property(&self, fp: &[i32]) -> (Option<&Property>, &Field, PathName); + fn set_property(&mut self, fp: &[i32], value: Option); +} + +pub type EntityFactory = &'static dyn Fn(Rc) -> Box; + +pub struct EntityList { + entities: Vec>>, + entity_factory: EntityFactory, /// Only used by read_props to avoid allocations. field_paths: Vec, } -impl Entities { +impl EntityList { + pub fn new(entity_factory: EntityFactory) -> Self { + Self { + entities: Default::default(), + entity_factory, + field_paths: Vec::with_capacity(512), + } + } + pub fn len(&self) -> usize { self.entities.len() } @@ -54,7 +71,7 @@ impl Entities { (false, false) => { trace!("Update entity {entity_id}"); if let Some(entity) = self.entities[entity_id as usize].as_mut() { - entity.read_props(&mut reader, &mut self.field_paths)?; + Self::read_props(&mut reader, entity.as_mut(), &mut self.field_paths)?; } else { return Err(Error::InvalidEntityId); } @@ -65,12 +82,16 @@ impl Entities { reader.read_varuint32()?; // Don't know what this is. let class = classes.class(class_id); trace!("Create entity {entity_id} {}", class.serializer.name); - let mut entity = Entity::new(Rc::clone(&class.serializer)); + let mut entity = (self.entity_factory)(Rc::clone(&class.serializer)); if let Some(baseline) = &class.instance_baseline { - entity.read_props(&mut BitReader::new(baseline), &mut self.field_paths)?; + Self::read_props( + &mut BitReader::new(baseline), + entity.as_mut(), + &mut self.field_paths, + )?; trace!("Baseline for entity {entity_id} done"); }; - entity.read_props(&mut reader, &mut self.field_paths)?; + Self::read_props(&mut reader, entity.as_mut(), &mut self.field_paths)?; if self.entities.len() <= entity_id as usize { self.entities.resize_with(entity_id as usize + 1, || None); } @@ -84,103 +105,13 @@ impl Entities { } Ok(()) } -} - -impl std::ops::Index for Entities { - type Output = Entity; - - /// Returns a reference to the entity with the supplied id. - /// - /// # Panics - /// - /// Panics if the id is not found. - fn index(&self, id: usize) -> &Self::Output { - self.entities[id].as_ref().expect("no entry for index") - } -} - -#[derive(Debug)] -pub struct Entity { - object: Object, - serializer: Rc, -} - -impl Entity { - fn new(serializer: Rc) -> Self { - Self { - object: Object::new(&serializer), - serializer, - } - } - - pub fn get_property(&self, fp: &[i32]) -> (Option<&Property>, &Field, PathName) { - let prop = self.object.properties[fp[0] as usize].as_ref(); - let field = &self.serializer.fields[fp[0] as usize]; - let name = PathName { - items: vec![PathNameItem::Field(field.name())], - }; - fp[1..] - .iter() - .fold((prop, field, name), |(prop, field, name), &i| { - let i = i as usize; - let is_array = matches!(field, Field::Array(_) | Field::Vector(_)); - let (prop, field) = match (prop, field) { - (Some(Property::Object(o)), Field::Object(f)) => { - (o.properties[i].as_ref(), &f.serializer.fields[i]) - } - (Some(Property::Array(a)), Field::Array(f)) => { - (a[i].as_ref(), f.element.as_ref()) - } - (Some(Property::Array(a)), Field::Vector(f)) => { - (a[i].as_ref(), f.element.as_ref()) - } - (None, f) => (None, f), - (Some(p), f) => unreachable!("{p:?} {f:?}"), - }; - let name = if is_array { - name.push_index(i) - } else { - name.push_field(field.name()) - }; - (prop, field, name) - }) - } - - fn property(&mut self, fp: &[i32]) -> (&mut Option, &Field) { - let prop = &mut self.object.properties[fp[0] as usize]; - let field = &self.serializer.fields[fp[0] as usize]; - fp[1..] - .iter() - .fold((prop, field), |(prop, field), &i| match (prop, field) { - (Some(Property::Object(o)), Field::Object(f)) => ( - &mut o.properties[i as usize], - &f.serializer.fields[i as usize], - ), - (Some(Property::Array(a)), Field::Array(f)) => { - (&mut a[i as usize], f.element.as_ref()) - } - (Some(Property::Array(a)), Field::Vector(f)) => { - if a.len() <= i as usize { - a.resize(i as usize + 1, Default::default()); - } - (&mut a[i as usize], f.element.as_ref()) - } - (Some(p), f) => unreachable!("{p:?} {f:?}"), - (p, Field::Array(f)) if p.is_none() => { - *p = Some(Property::Array( - vec![None; f.size as usize], - )); - match p { - Some(Property::Array(a)) => (&mut a[i as usize], &f.element.as_ref()), - _ => unreachable!(), - } - } - (None, f) => unreachable!("{f:?}"), - }) - } /// Read props from `reader`, creating new props or overwriting existing ones. - fn read_props(&mut self, reader: &mut BitReader, fps: &mut Vec) -> Result<()> { + fn read_props( + reader: &mut BitReader, + entity: &mut dyn Entity, + fps: &mut Vec, + ) -> Result<()> { let mut fp = FieldPath::new(); fps.clear(); loop { @@ -190,12 +121,13 @@ impl Entity { } fps.push(fp.clone()); } + let serializer = Rc::clone(entity.serializer()); for fp in fps { - let (prop, field) = self.property(fp.data()); - *prop = (field.decoder())(reader)?; + let field = get_field(serializer.as_ref(), fp.data()); + entity.set_property(fp.data(), field.decoder().decode(reader)?); if enabled!(Level::TRACE) { - let (prop, field, name) = self.get_property(fp.data()); + let (prop, field, name) = entity.get_property(fp.data()); match field { Field::Value(_) | Field::Array(_) | Field::Vector(_) => { trace!("{fp} {}: {} = {}", name, field.ctype(), prop.unwrap()) @@ -210,95 +142,29 @@ impl Entity { } } -impl fmt::Display for Entity { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fn dfs( - f: &mut fmt::Formatter<'_>, - path: PathName, - prop: &Property, - field: &Field, - ) -> fmt::Result { - let path = path.push_field(field.name()); - match (prop, field) { - (Property::Object(o), Field::Object(fo)) => { - print_object(f, path, o, &fo.serializer)? - } - (Property::Array(a), Field::Array(fa)) => { - for (i, e) in a.iter().enumerate() { - let path = path.clone().push_index(i); - dfs(f, path, e.as_ref().unwrap(), &fa.element)?; - } - } - (Property::Array(a), Field::Vector(fv)) => { - for (i, e) in a.iter().enumerate() { - let path = path.clone().push_index(i); - dfs(f, path, e.as_ref().unwrap(), &fv.element)?; - } - } - _ => writeln!(f, "{} = {}", path, prop)?, - } - Ok(()) - } - - fn print_object( - f: &mut fmt::Formatter<'_>, - path: PathName, - object: &Object, - serializer: &Serializer, - ) -> fmt::Result { - for (i, e) in object.properties.iter().enumerate() { - let field = &serializer.fields[i]; - if let Some(prop) = e { - dfs(f, path.clone(), prop, field)?; - } - } - Ok(()) - } - - let path = PathName { - items: vec![PathNameItem::Field(Rc::from(self.serializer.name.as_str()))], - }; - print_object(f, path, &self.object, &self.serializer) - } -} - -#[derive(Clone)] -enum PathNameItem { - Field(Rc), - Index(usize), +fn get_field<'a>(serializer: &'a Serializer, fp: &[i32]) -> &'a Field { + let field = &serializer.fields[fp[0] as usize]; + fp[1..].iter().fold(field, |field, &i| match field { + Field::Object(f) => &f.serializer.fields[i as usize], + Field::Array(f) => f.element.as_ref(), + Field::Vector(f) => f.element.as_ref(), + Field::Value(_) => unreachable!(), + }) } -#[derive(Clone)] -pub struct PathName { - items: Vec, -} - -impl PathName { - fn push_field(mut self, field: Rc) -> Self { - self.items.push(PathNameItem::Field(field)); - self - } +impl std::ops::Index for EntityList { + type Output = dyn Entity; - fn push_index(mut self, index: usize) -> Self { - self.items.push(PathNameItem::Index(index)); - self - } -} - -impl std::fmt::Display for PathName { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - for (idx, item) in self.items.iter().enumerate() { - match item { - PathNameItem::Field(field) => { - if idx > 0 { - write!(f, ".")? - } - write!(f, "{field}")?; - } - PathNameItem::Index(index) => write!(f, ".{index:04}")?, - } - } - Ok(()) + /// Returns a reference to the entity with the supplied id. + /// + /// # Panics + /// + /// Panics if the id is not found. + fn index(&self, id: usize) -> &Self::Output { + self.entities[id] + .as_ref() + .expect("no entry for index") + .as_ref() } } @@ -322,7 +188,7 @@ mod tests { } } - let mut entities = Entities::default(); + let mut entities = EntityList::new(&TreeEntity::factory); entities.read_packet_entities(testdata::packet_entities(), &classes)?; Ok(()) } diff --git a/cs2-demo/src/entity/decoder.rs b/cs2-demo/src/entity/decoder.rs index 0dcc692..bb8718a 100644 --- a/cs2-demo/src/entity/decoder.rs +++ b/cs2-demo/src/entity/decoder.rs @@ -2,159 +2,143 @@ use std::rc::Rc; use bitstream_io::BitRead; -use super::{send_tables::Serializer, Object, Property}; +use super::{property::Object, send_tables::Serializer, Property}; use crate::{read::ValveBitReader, BitReader}; -pub(super) type Decoder = Rc std::io::Result>>; - -pub(super) struct DecoderCache { - pub(super) noscale: Decoder, - pub(super) simtime: Decoder, - pub(super) bool: Decoder, - pub(super) coord: Decoder, - pub(super) unsigned: Decoder, - pub(super) signed: Decoder, - pub(super) string: Decoder, - pub(super) qangle_precise: Decoder, - pub(super) qangle_coord: Decoder, - pub(super) uint64: Decoder, - pub(super) fixed64: Decoder, - pub(super) normal_vec: Decoder, -} - -impl DecoderCache { - pub(super) fn new() -> Self { - Self { - noscale: Rc::new(decode_noscale), - simtime: Rc::new(decode_simtime), - coord: Rc::new(decode_coord), - bool: Rc::new(decode_bool), - unsigned: Rc::new(decode_unsigned), - signed: Rc::new(decode_signed), - string: Rc::new(decode_string), - qangle_precise: Rc::new(decode_qangle_precise), - qangle_coord: Rc::new(decode_qangle_coord), - uint64: Rc::new(decode_uint64), - fixed64: Rc::new(decode_fixed64), - normal_vec: Rc::new(decode_normal_vec), - } - } - - pub(super) fn decode_float32( - &self, - encoder: Option<&str>, - bit_count: i32, - low: f32, - high: f32, - flags: i32, - ) -> Decoder { - match encoder { - Some("coord") => Rc::clone(&self.coord), - Some("simtime") => Rc::clone(&self.simtime), - Some(_) => todo!(), - None => { - if bit_count == 0 || bit_count >= 32 { - assert!(flags == 0 && low == 0.0 && high == 1.0); - return Rc::clone(&self.noscale); - } - return decode_quantized(bit_count, low, high, flags); - } - } - } +#[derive(Clone)] +pub(super) enum Decoder { + None, + Quantized(QuantizedParams), + NoScale, + Simtime, + Bool, + Coord, + I32, + U32, + U64, + Fixed64, + String, + QAnglePrecise, + QAngleCoord, + QAngle(u32), + VectorNormal, + VectorCoord(u8), + VectorNoScale(u8), + Object(Rc), + Polymorphic(Rc), +} - pub(super) fn decode_qangle(&self, encoder: Option<&str>, bit_count: i32) -> Decoder { - match encoder { - Some("qangle_precise") => Rc::clone(&self.qangle_precise), - Some("qangle") => { - if bit_count != 0 { - Rc::new(move |reader| { - Ok(Some(Property::Vec3([ - reader.read_angle(bit_count as u32)?, - reader.read_angle(bit_count as u32)?, - reader.read_angle(bit_count as u32)?, - ]))) - }) - } else { - Rc::clone(&self.qangle_coord) - } - } - Some(s) => todo!("{}", s), - None => todo!(), +impl Decoder { + pub(super) fn decode(&self, reader: &mut BitReader) -> std::io::Result> { + match self { + Decoder::Quantized(qp) => decode_quantized(reader, qp), + Decoder::NoScale => decode_noscale(reader), + Decoder::Simtime => decode_simtime(reader), + Decoder::Bool => decode_bool(reader), + Decoder::Coord => decode_coord(reader), + Decoder::I32 => decode_signed(reader), + Decoder::U32 => decode_unsigned(reader), + Decoder::U64 => decode_uint64(reader), + Decoder::Fixed64 => decode_fixed64(reader), + Decoder::String => decode_string(reader), + Decoder::QAnglePrecise => decode_qangle_precise(reader), + Decoder::QAngleCoord => decode_qangle_coord(reader), + Decoder::QAngle(bit_count) => decode_qangle3(reader, *bit_count), + Decoder::VectorNormal => decode_vector_normal(reader), + Decoder::VectorCoord(size) => decode_vector_coord(reader, *size), + Decoder::VectorNoScale(size) => decode_vector_noscale(reader, *size), + Decoder::Object(serializer) => decode_object(reader, serializer), + Decoder::Polymorphic(serializer) => decode_polymorphic(reader, serializer), + Decoder::None => unreachable!(), } } +} - pub(super) fn decode_object(&self, serializer: Rc) -> Decoder { - Rc::new(move |reader| { - if reader.read_bit()? { - let object = Object::new(&serializer); - Ok(Some(Property::Object(object))) +pub(super) fn decode_float32( + encoder: Option<&str>, + bit_count: i32, + low: f32, + high: f32, + flags: i32, +) -> Decoder { + match encoder { + Some("coord") => Decoder::Coord, + Some("simtime") => Decoder::Simtime, + Some(_) => todo!(), + None => { + if bit_count == 0 || bit_count >= 32 { + assert!(flags == 0 && low == 0.0 && high == 1.0); + Decoder::NoScale } else { - Ok(None) + Decoder::Quantized(quantized_params(bit_count, low, high, flags)) } - }) + } } +} - pub(super) fn decode_polymorphic(&self, serializer: Rc) -> Decoder { - Rc::new(move |reader| { - if reader.read_bit()? { - let _polymorphic_index = reader.read_ubitvar()?; - // TODO this should create an object based on _polymorphic_index - let object = Object::new(&serializer); - Ok(Some(Property::Object(object))) +pub(super) fn decode_qangle(encoder: Option<&str>, bit_count: i32) -> Decoder { + match encoder { + Some("qangle_precise") => Decoder::QAnglePrecise, + Some("qangle") => { + if bit_count != 0 { + Decoder::QAngle(bit_count as u32) } else { - Ok(None) + Decoder::QAngleCoord } - }) + } + Some(s) => todo!("{}", s), + None => todo!(), } +} - pub(super) fn decode_fixed_array(&self, size: u16) -> Decoder { - Rc::new(move |_| Ok(Some(Property::Array(vec![None; size as usize])))) +pub(super) fn decode_object( + reader: &mut BitReader, + serializer: &Rc, +) -> std::io::Result> { + if reader.read_bit()? { + let object = Object::new(serializer); + Ok(Some(Property::Object(object))) + } else { + Ok(None) } +} - pub(super) fn decode_var_array(&self, init: Option) -> Decoder { - Rc::new(move |r| { - let vec = vec![init.clone(); r.read_varuint32()? as usize]; - Ok(Some(Property::Array(vec))) - }) +pub(super) fn decode_polymorphic( + reader: &mut BitReader, + serializer: &Rc, +) -> std::io::Result> { + if reader.read_bit()? { + let _polymorphic_index = reader.read_ubitvar()?; + // TODO this should create an object based on _polymorphic_index + let object = Object::new(serializer); + Ok(Some(Property::Object(object))) + } else { + Ok(None) } +} - pub(crate) fn decode_vector( - &self, - size: u8, - encoder: Option<&str>, - bit_count: i32, - low_value: f32, - high_value: f32, - encode_flags: i32, - ) -> Decoder { - assert!( - (bit_count == 0 || bit_count == 32) - && low_value == 0.0 - && high_value == 1.0 - && encode_flags == 0 - ); - match encoder { - Some("normal") => { - assert!(size == 3); - Rc::clone(&self.normal_vec) - } - // TODO: cache - Some("coord") => match size { - 2 => Rc::new(|r| decode_vector2(r, decode_coord_f32)), - 3 => Rc::new(|r| decode_vector3(r, decode_coord_f32)), - 4 => Rc::new(|r| decode_vector4(r, decode_coord_f32)), - 6 => Rc::new(|r| decode_vector6(r, decode_coord_f32)), - _ => unreachable!(), - }, - None => match size { - 2 => Rc::new(|r| decode_vector2(r, decode_noscale_f32)), - 3 => Rc::new(|r| decode_vector3(r, decode_noscale_f32)), - 4 => Rc::new(|r| decode_vector4(r, decode_noscale_f32)), - 6 => Rc::new(|r| decode_vector6(r, decode_noscale_f32)), - _ => unreachable!(), - }, - _ => unimplemented!("vector encoder {}", encoder.unwrap()), +pub(crate) fn decode_vector( + size: u8, + encoder: Option<&str>, + bit_count: i32, + low_value: f32, + high_value: f32, + encode_flags: i32, +) -> Decoder { + assert!( + (bit_count == 0 || bit_count == 32) + && low_value == 0.0 + && high_value == 1.0 + && encode_flags == 0 + ); + match encoder { + Some("normal") => { + assert!(size == 3); + Decoder::VectorNormal } + Some("coord") => Decoder::VectorCoord(size), + None => Decoder::VectorNoScale(size), + _ => unimplemented!("vector encoder {}", encoder.unwrap()), } } @@ -179,28 +163,28 @@ fn decode_coord(reader: &mut BitReader) -> std::io::Result> { Ok(Some(Property::F32(decode_coord_f32(reader)?))) } -fn decode_bool(r: &mut BitReader) -> std::io::Result> { - Ok(Some(Property::Bool(r.read_bit()?))) +fn decode_bool(reader: &mut BitReader) -> std::io::Result> { + Ok(Some(Property::Bool(reader.read_bit()?))) } -fn decode_unsigned(r: &mut BitReader) -> std::io::Result> { - Ok(Some(Property::U32(r.read_varuint32()?))) +fn decode_unsigned(reader: &mut BitReader) -> std::io::Result> { + Ok(Some(Property::U32(reader.read_varuint32()?))) } -fn decode_signed(r: &mut BitReader) -> std::io::Result> { - Ok(Some(Property::I32(r.read_signed_varint32()?))) +fn decode_signed(reader: &mut BitReader) -> std::io::Result> { + Ok(Some(Property::I32(reader.read_signed_varint32()?))) } -fn decode_uint64(r: &mut BitReader) -> std::io::Result> { - Ok(Some(Property::U64(r.read_varuint64()?))) +fn decode_uint64(reader: &mut BitReader) -> std::io::Result> { + Ok(Some(Property::U64(reader.read_varuint64()?))) } -fn decode_fixed64(r: &mut BitReader) -> std::io::Result> { - Ok(Some(Property::U64(r.read::(64)?))) +fn decode_fixed64(reader: &mut BitReader) -> std::io::Result> { + Ok(Some(Property::U64(reader.read::(64)?))) } -fn decode_string(r: &mut BitReader) -> std::io::Result> { - Ok(Some(Property::Str(Box::from(r.read_string()?)))) +fn decode_string(reader: &mut BitReader) -> std::io::Result> { + Ok(Some(Property::Str(Box::from(reader.read_string()?)))) } const ROUNDDOWN: i32 = 1 << 0; @@ -208,28 +192,18 @@ const ROUNDUP: i32 = 1 << 1; const ENCODE_ZERO_EXACTLY: i32 = 1 << 2; const ENCODE_INTEGERS_EXACTLY: i32 = 1 << 3; -fn decode_quantized(bit_count: i32, low: f32, high: f32, flags: i32) -> Decoder { - let QuantizedParams { - bit_count, - low, - high, - flags, - decode_mul, - } = quantized_params(bit_count, low, high, flags); - - Rc::new(move |r: &mut BitReader| { - let val = if flags & ROUNDDOWN != 0 && r.read_bit()? { - low - } else if flags & ROUNDUP != 0 && r.read_bit()? { - high - } else if flags & ENCODE_ZERO_EXACTLY != 0 && r.read_bit()? { - 0f32 - } else { - let u = r.read::(bit_count as u32)?; - low + (high - low) * (u as f32 * decode_mul) - }; - Ok(Some(Property::F32(val))) - }) +fn decode_quantized(r: &mut BitReader, p: &QuantizedParams) -> std::io::Result> { + let val = if p.flags & ROUNDDOWN != 0 && r.read_bit()? { + p.low + } else if p.flags & ROUNDUP != 0 && r.read_bit()? { + p.high + } else if p.flags & ENCODE_ZERO_EXACTLY != 0 && r.read_bit()? { + 0f32 + } else { + let u = r.read::(p.bit_count as u32)?; + p.low + (p.high - p.low) * (u as f32 * p.decode_mul) + }; + Ok(Some(Property::F32(val))) } fn validate_flags(low: f32, high: f32, mut flags: i32) -> i32 { @@ -254,8 +228,8 @@ fn validate_flags(low: f32, high: f32, mut flags: i32) -> i32 { flags } -#[derive(Debug, PartialEq)] -struct QuantizedParams { +#[derive(Clone, Debug, PartialEq)] +pub(super) struct QuantizedParams { bit_count: i32, low: f32, high: f32, @@ -345,51 +319,59 @@ fn quantized_params( } } -fn decode_qangle_precise(r: &mut BitReader) -> std::io::Result> { +fn decode_qangle3(reader: &mut BitReader, bit_count: u32) -> std::io::Result> { + Ok(Some(Property::Vec3([ + reader.read_angle(bit_count)?, + reader.read_angle(bit_count)?, + reader.read_angle(bit_count)?, + ]))) +} + +fn decode_qangle_precise(reader: &mut BitReader) -> std::io::Result> { let mut vec = [0.0; 3]; - let has_x = r.read_bit()?; - let has_y = r.read_bit()?; - let has_z = r.read_bit()?; + let has_x = reader.read_bit()?; + let has_y = reader.read_bit()?; + let has_z = reader.read_bit()?; if has_x { - vec[0] = r.read_angle(20)? - 180.0; + vec[0] = reader.read_angle(20)? - 180.0; } if has_y { - vec[1] = r.read_angle(20)? - 180.0; + vec[1] = reader.read_angle(20)? - 180.0; } if has_z { - vec[2] = r.read_angle(20)? - 180.0; + vec[2] = reader.read_angle(20)? - 180.0; } Ok(Some(Property::Vec3(vec))) } -fn decode_qangle_coord(r: &mut BitReader) -> std::io::Result> { +fn decode_qangle_coord(reader: &mut BitReader) -> std::io::Result> { let mut vec = [0.0; 3]; - let has_x = r.read_bit()?; - let has_y = r.read_bit()?; - let has_z = r.read_bit()?; + let has_x = reader.read_bit()?; + let has_y = reader.read_bit()?; + let has_z = reader.read_bit()?; if has_x { - vec[0] = r.read_coord()?; + vec[0] = reader.read_coord()?; } if has_y { - vec[1] = r.read_coord()?; + vec[1] = reader.read_coord()?; } if has_z { - vec[2] = r.read_coord()?; + vec[2] = reader.read_coord()?; } Ok(Some(Property::Vec3(vec))) } -fn decode_normal_vec(r: &mut BitReader) -> std::io::Result> { +fn decode_vector_normal(reader: &mut BitReader) -> std::io::Result> { let mut vec = [0.0; 3]; - let has_x = r.read_bit()?; - let has_y = r.read_bit()?; + let has_x = reader.read_bit()?; + let has_y = reader.read_bit()?; if has_x { - vec[0] = r.read_normal()?; + vec[0] = reader.read_normal()?; } if has_y { - vec[1] = r.read_normal()?; + vec[1] = reader.read_normal()?; } - let neg_z = r.read_bit()?; + let neg_z = reader.read_bit()?; let prod_sum = vec[0] * vec[0] + vec[1] * vec[1]; if prod_sum < 1.0 { vec[2] = (1.0 - prod_sum).sqrt(); @@ -404,6 +386,26 @@ fn decode_normal_vec(r: &mut BitReader) -> std::io::Result> { type DecodeF32 = fn(r: &mut BitReader) -> std::io::Result; +fn decode_vector_coord(r: &mut BitReader, size: u8) -> std::io::Result> { + match size { + 2 => decode_vector2(r, decode_coord_f32), + 3 => decode_vector3(r, decode_coord_f32), + 4 => decode_vector4(r, decode_coord_f32), + 6 => decode_vector6(r, decode_coord_f32), + _ => unreachable!(), + } +} + +fn decode_vector_noscale(r: &mut BitReader, size: u8) -> std::io::Result> { + match size { + 2 => decode_vector2(r, decode_noscale_f32), + 3 => decode_vector3(r, decode_noscale_f32), + 4 => decode_vector4(r, decode_noscale_f32), + 6 => decode_vector6(r, decode_noscale_f32), + _ => unreachable!(), + } +} + fn decode_vector2(r: &mut BitReader, decoder: DecodeF32) -> std::io::Result> { let vec = [decoder(r)?, decoder(r)?]; Ok(Some(Property::Vec2(vec))) diff --git a/cs2-demo/src/entity/path_name.rs b/cs2-demo/src/entity/path_name.rs new file mode 100644 index 0000000..8648043 --- /dev/null +++ b/cs2-demo/src/entity/path_name.rs @@ -0,0 +1,41 @@ +use std::rc::Rc; + +#[derive(Clone)] +pub(super) enum PathNameItem { + Field(Rc), + Index(usize), +} + +#[derive(Clone)] +pub struct PathName { + pub(super) items: Vec, +} + +impl PathName { + pub(super) fn push_field(mut self, field: Rc) -> Self { + self.items.push(PathNameItem::Field(field)); + self + } + + pub(super) fn push_index(mut self, index: usize) -> Self { + self.items.push(PathNameItem::Index(index)); + self + } +} + +impl std::fmt::Display for PathName { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for (idx, item) in self.items.iter().enumerate() { + match item { + PathNameItem::Field(field) => { + if idx > 0 { + write!(f, ".")? + } + write!(f, "{field}")?; + } + PathNameItem::Index(index) => write!(f, ".{index:04}")?, + } + } + Ok(()) + } +} diff --git a/cs2-demo/src/entity/property.rs b/cs2-demo/src/entity/property.rs index 73a5e22..f92340b 100644 --- a/cs2-demo/src/entity/property.rs +++ b/cs2-demo/src/entity/property.rs @@ -1,6 +1,8 @@ -use std::rc::Rc; +use std::{fmt, rc::Rc}; -use super::send_tables::Serializer; +use super::path_name::PathNameItem; +use super::send_tables::{Field, Serializer}; +use super::{Entity, PathName}; #[derive(Debug, Clone)] pub struct Object { @@ -50,3 +52,159 @@ impl std::fmt::Display for Property { } } } + +#[derive(Debug)] +pub struct TreeEntity { + object: Object, + serializer: Rc, +} + +impl TreeEntity { + pub fn factory(serializer: Rc) -> Box { + Box::new(Self { + object: Object::new(&serializer), + serializer, + }) + } +} + +impl Entity for TreeEntity { + fn serializer(&self) -> &Rc { + &self.serializer + } + + fn get_property(&self, fp: &[i32]) -> (Option<&Property>, &Field, PathName) { + let prop = self.object.properties[fp[0] as usize].as_ref(); + let field = &self.serializer.fields[fp[0] as usize]; + let name = PathName { + items: vec![PathNameItem::Field(field.name())], + }; + fp[1..] + .iter() + .fold((prop, field, name), |(prop, field, name), &i| { + let i = i as usize; + let is_array = matches!(field, Field::Array(_) | Field::Vector(_)); + let (prop, field) = match (prop, field) { + (Some(Property::Object(o)), Field::Object(f)) => { + (o.properties[i].as_ref(), &f.serializer.fields[i]) + } + (Some(Property::Array(a)), Field::Array(f)) => { + (a[i].as_ref(), f.element.as_ref()) + } + (Some(Property::Array(a)), Field::Vector(f)) => { + (a[i].as_ref(), f.element.as_ref()) + } + (None, f) => (None, f), + (Some(p), f) => unreachable!("{p:?} {f:?}"), + }; + let name = if is_array { + name.push_index(i) + } else { + name.push_field(field.name()) + }; + (prop, field, name) + }) + } + + fn set_property(&mut self, fp: &[i32], value: Option) { + let prop = &mut self.object.properties[fp[0] as usize]; + let field = &self.serializer.fields[fp[0] as usize]; + let (prop, field) = + fp[1..] + .iter() + .fold((prop, field), |(prop, field), &i| match (prop, field) { + (Some(Property::Object(o)), Field::Object(f)) => ( + &mut o.properties[i as usize], + &f.serializer.fields[i as usize], + ), + (Some(Property::Array(a)), Field::Array(f)) => { + (&mut a[i as usize], f.element.as_ref()) + } + (p, Field::Array(f)) if p.is_none() => { + *p = Some(Property::Array(vec![None; f.size as usize])); + match p { + Some(Property::Array(a)) => (&mut a[i as usize], &f.element.as_ref()), + _ => unreachable!(), + } + } + (Some(Property::Array(a)), Field::Vector(f)) => { + if a.len() <= i as usize { + a.resize(i as usize + 1, Default::default()); + } + (&mut a[i as usize], f.element.as_ref()) + } + (Some(p), f) => unreachable!("{p:?} {f:?}"), + (None, f) => unreachable!("{f:?}"), + }); + match field { + Field::Value(_) => *prop = value, + Field::Object(_) => *prop = value, + Field::Array(_) => *prop = value, + Field::Vector(v) => { + let size = match value { + Some(Property::U32(size)) => size, + _ => unreachable!(), + }; + let init = match v.element.as_ref() { + Field::Value(_) => None, + Field::Object(o) => Some(Property::Object(Object::new(&o.serializer))), + Field::Array(_) | Field::Vector(_) => unimplemented!(), + }; + let vec = vec![init.clone(); size as usize]; + *prop = Some(Property::Array(vec)); + } + }; + } +} + +impl fmt::Display for TreeEntity { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fn dfs( + f: &mut fmt::Formatter<'_>, + path: PathName, + prop: &Property, + field: &Field, + ) -> fmt::Result { + let path = path.push_field(field.name()); + match (prop, field) { + (Property::Object(o), Field::Object(fo)) => { + print_object(f, path, o, &fo.serializer)? + } + (Property::Array(a), Field::Array(fa)) => { + for (i, e) in a.iter().enumerate() { + let path = path.clone().push_index(i); + dfs(f, path, e.as_ref().unwrap(), &fa.element)?; + } + } + (Property::Array(a), Field::Vector(fv)) => { + for (i, e) in a.iter().enumerate() { + let path = path.clone().push_index(i); + dfs(f, path, e.as_ref().unwrap(), &fv.element)?; + } + } + _ => writeln!(f, "{} = {}", path, prop)?, + } + Ok(()) + } + + fn print_object( + f: &mut fmt::Formatter<'_>, + path: PathName, + object: &Object, + serializer: &Serializer, + ) -> fmt::Result { + for (i, e) in object.properties.iter().enumerate() { + let field = &serializer.fields[i]; + if let Some(prop) = e { + dfs(f, path.clone(), prop, field)?; + } + } + Ok(()) + } + + let path = PathName { + items: vec![PathNameItem::Field(Rc::from(self.serializer.name.as_str()))], + }; + print_object(f, path, &self.object, &self.serializer) + } +} diff --git a/cs2-demo/src/entity/send_tables.rs b/cs2-demo/src/entity/send_tables.rs index b2f4839..bb5b028 100644 --- a/cs2-demo/src/entity/send_tables.rs +++ b/cs2-demo/src/entity/send_tables.rs @@ -8,8 +8,7 @@ use crate::proto::demo::CDemoSendTables; use crate::proto::netmessages::CSVCMsg_FlattenedSerializer; use crate::{Error, Result}; -use super::decoder::{Decoder, DecoderCache}; -use super::{Object, Property}; +use super::decoder::{decode_float32, decode_qangle, decode_vector, Decoder}; #[derive(Debug, Default)] pub struct SendTables { @@ -17,7 +16,7 @@ pub struct SendTables { } #[derive(Debug)] -pub(super) struct Serializer { +pub struct Serializer { pub(super) name: String, pub(super) fields: Vec, } @@ -34,14 +33,14 @@ impl std::fmt::Display for Serializer { #[derive(Clone)] pub struct ValueField { + decoder: Decoder, var_name: Rc, var_type: Rc, - decoder: Decoder, } #[derive(Clone)] pub struct ArrayField { - pub(super) size: u16, decoder: Decoder, + pub(super) size: u16, pub(super) element: Box, } #[derive(Clone)] @@ -51,9 +50,9 @@ pub struct VectorField { } #[derive(Clone)] pub struct ObjectField { + decoder: Decoder, var_name: Rc, var_type: Rc, - decoder: Decoder, pub(super) serializer: Rc, } @@ -84,7 +83,7 @@ impl Field { } } - pub fn decoder(&self) -> &Decoder { + pub(super) fn decoder(&self) -> &Decoder { match self { Field::Value(v) => &v.decoder, Field::Object(v) => &v.decoder, @@ -169,18 +168,12 @@ impl SentTableData { struct SendTableBuilder<'a> { data: &'a SentTableData, serializers: Vec>>, - decoder_cache: DecoderCache, } impl<'a> SendTableBuilder<'a> { fn new(data: &'a SentTableData) -> Self { let serializers = vec![None; data.fs.serializers.len()]; - let decoder_cache = DecoderCache::new(); - Self { - data, - serializers, - decoder_cache, - } + Self { data, serializers } } fn build(mut self) -> Result { @@ -255,19 +248,18 @@ impl<'a> SendTableBuilder<'a> { let ctype = CType::parse(var_type.as_ref()); let field = if let Some(serializer) = serializer { let decoder = if !polymorphic_types.is_empty() { - self.decoder_cache.decode_polymorphic(serializer.clone()) + Decoder::Polymorphic(serializer.clone()) } else { - self.decoder_cache.decode_object(serializer.clone()) + Decoder::Object(serializer.clone()) }; Field::Object(ObjectField { - var_name, + var_name: Rc::clone(&var_name), var_type: Rc::clone(&var_type), decoder, serializer, }) } else { let decoder = make_decoder( - &self.decoder_cache, ctype.0, encoder, fsf.bit_count(), @@ -276,42 +268,34 @@ impl<'a> SendTableBuilder<'a> { fsf.encode_flags(), ); Field::Value(ValueField { - var_name, + var_name: Rc::clone(&var_name), var_type: Rc::clone(&var_type), decoder, }) }; match ctype.2 { ArraySize::None => field, - ArraySize::Fixed(size) => { - let decoder = match ctype.0 { - "char" => Rc::clone(&self.decoder_cache.string), - _ => self.decoder_cache.decode_fixed_array(size), - }; - Field::Array(ArrayField { + ArraySize::Fixed(size) => match ctype.0 { + "char" => Field::Value(ValueField { + var_name, + var_type, + decoder: Decoder::String, + }), + _ => Field::Array(ArrayField { size, - decoder, - element: Box::new(field), - }) - } - ArraySize::Variable => { - let init = match &field { - Field::Value(_) => None, - Field::Object(o) => Some(Property::Object(Object::new(&o.serializer))), - Field::Array(_) | Field::Vector(_) => todo!(), - }; - let decoder = self.decoder_cache.decode_var_array(init); - Field::Vector(VectorField { - decoder, + decoder: Decoder::None, element: Box::new(field), - }) - } + }), + }, + ArraySize::Variable => Field::Vector(VectorField { + decoder: Decoder::U32, + element: Box::new(field), + }), } } } fn make_decoder( - cache: &DecoderCache, base_type: &str, encoder: Option<&str>, bit_count: i32, @@ -326,11 +310,11 @@ fn make_decoder( | "BeamType_t" | "CEntityIndex" | "EntityDisolveType_t" - | "HSequence" => Rc::clone(&cache.signed), + | "HSequence" => Decoder::I32, "uint64" | "CStrongHandle" => match encoder { - Some("fixed64") => Rc::clone(&cache.fixed64), + Some("fixed64") => Decoder::Fixed64, Some(s) => todo!("{}", s), - None => Rc::clone(&cache.uint64), + None => Decoder::U64, }, "uint8" | "uint16" @@ -382,24 +366,20 @@ fn make_decoder( | "ValueRemapperOutputType_t" | "WeaponAttackType_t" | "WeaponState_t" - | "WorldGroupId_t" => Rc::clone(&cache.unsigned), - "bool" => Rc::clone(&cache.bool), + | "WorldGroupId_t" => Decoder::U32, + "bool" => Decoder::Bool, "float32" | "CNetworkedQuantizedFloat" => { - cache.decode_float32(encoder, bit_count, low_value, high_value, encode_flags) - } - "char" | "CUtlString" | "CUtlSymbolLarge" => Rc::clone(&cache.string), - "GameTime_t" => Rc::clone(&cache.noscale), - "QAngle" => cache.decode_qangle(encoder, bit_count), - "Vector2D" => { - cache.decode_vector(2, encoder, bit_count, low_value, high_value, encode_flags) + decode_float32(encoder, bit_count, low_value, high_value, encode_flags) } - "Vector" => cache.decode_vector(3, encoder, bit_count, low_value, high_value, encode_flags), + "char" | "CUtlString" | "CUtlSymbolLarge" => Decoder::String, + "GameTime_t" => Decoder::NoScale, + "QAngle" => decode_qangle(encoder, bit_count), + "Vector2D" => decode_vector(2, encoder, bit_count, low_value, high_value, encode_flags), + "Vector" => decode_vector(3, encoder, bit_count, low_value, high_value, encode_flags), "Vector4D" | "Quaternion" => { - cache.decode_vector(4, encoder, bit_count, low_value, high_value, encode_flags) - } - "CTransform" => { - cache.decode_vector(6, encoder, bit_count, low_value, high_value, encode_flags) + decode_vector(4, encoder, bit_count, low_value, high_value, encode_flags) } + "CTransform" => decode_vector(6, encoder, bit_count, low_value, high_value, encode_flags), _ => todo!("{}", base_type), } } diff --git a/cs2-demo/src/visit.rs b/cs2-demo/src/visit.rs index 610456e..4d5a832 100644 --- a/cs2-demo/src/visit.rs +++ b/cs2-demo/src/visit.rs @@ -3,7 +3,7 @@ use std::io::Read; use tracing::{trace, trace_span}; use crate::demo_command::{DemoCommand, DemoParser}; -use crate::entity::{Classes, Entities, SendTables}; +use crate::entity::{Classes, EntityFactory, EntityList, SendTables}; use crate::game_event::{parse_game_event_list, GameEventDescriptors}; use crate::message::Message; use crate::packet::Packet; @@ -30,7 +30,7 @@ pub trait Visitor { &mut self, _game_event: CMsgSource1LegacyGameEvent, _tick: Tick, - _entities: &Entities, + _entities: &EntityList, ) -> anyhow::Result<()> { Ok(()) } @@ -42,24 +42,33 @@ pub trait Visitor { } } -pub fn parse(read: &mut dyn Read, visitor: &mut dyn Visitor) -> Result<()> { - DemoVisit::new(DemoParser::try_new(read)?, visitor).parse() +pub fn parse( + read: &mut dyn Read, + visitor: &mut dyn Visitor, + entity_factory: EntityFactory, +) -> Result<()> { + DemoVisit::new(DemoParser::try_new(read)?, visitor, entity_factory).parse() } struct DemoVisit<'a> { parser: DemoParser<'a>, visitor: &'a mut dyn Visitor, - send_tables: Option, classes: Option, + entities: EntityList, // Information necessary to parse UpdateStringTable. string_tables: Vec>, - // Data from "instancebaselines" string table cached until `classes` gets created. + // Stored temporarily until ClassInfo. + send_tables: Option, + // Data from "instancebaselines" string table stored temporarily until `classes` gets created. instance_baselines: StringTableData, - entities: Entities, } impl<'a> DemoVisit<'a> { - fn new(parser: DemoParser<'a>, visitor: &'a mut dyn Visitor) -> Self { + fn new( + parser: DemoParser<'a>, + visitor: &'a mut dyn Visitor, + entity_factory: EntityFactory, + ) -> Self { Self { parser, visitor, @@ -67,7 +76,7 @@ impl<'a> DemoVisit<'a> { classes: None, string_tables: Default::default(), instance_baselines: Default::default(), - entities: Default::default(), + entities: EntityList::new(entity_factory), } } } diff --git a/csdemoparser/src/cs2.rs b/csdemoparser/src/cs2.rs index b3c0f75..b4c41b4 100644 --- a/csdemoparser/src/cs2.rs +++ b/csdemoparser/src/cs2.rs @@ -7,7 +7,7 @@ use crate::game_event::GameEvent; use crate::last_jump::LastJump; use crate::Tick; use crate::{DemoInfo, Slot, UserId}; -use cs2_demo::entity::Entities; +use cs2_demo::entity::{EntityList, TreeEntity}; use cs2_demo::proto::demo::CDemoFileHeader; use cs2_demo::proto::gameevents::CMsgSource1LegacyGameEvent; use cs2_demo::{GameEventDescriptors, UserInfo, Visitor}; @@ -16,7 +16,7 @@ use tracing::{instrument, trace}; pub fn parse(read: &mut dyn std::io::Read) -> anyhow::Result { let mut state = GameState::new(); - cs2_demo::parse(read, &mut state)?; + cs2_demo::parse(read, &mut state, &TreeEntity::factory)?; state.get_info() } @@ -60,7 +60,7 @@ impl Visitor for GameState { &mut self, event: CMsgSource1LegacyGameEvent, tick: Tick, - _entities: &Entities, + _entities: &EntityList, ) -> anyhow::Result<()> { if let Some(descriptor) = self.game_event_descriptors.get(&event.eventid()) { let event = cs2_demo::game_event::de::from_proto(event, descriptor)?;