Skip to content

Commit

Permalink
Make Entity a trait to allow cs2-demo users to define their own Entit…
Browse files Browse the repository at this point in the history
…y structs.
  • Loading branch information
abenea committed Feb 25, 2024
1 parent f2082e6 commit 009fb12
Show file tree
Hide file tree
Showing 7 changed files with 513 additions and 457 deletions.
256 changes: 61 additions & 195 deletions cs2-demo/src/entity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,50 @@ 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;
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<Option<Entity>>,
pub trait Entity: std::fmt::Display {
fn serializer(&self) -> &Rc<Serializer>;
fn get_property(&self, fp: &[i32]) -> (Option<&Property>, &Field, PathName);
fn set_property(&mut self, fp: &[i32], value: Option<Property>);
}

pub type EntityFactory = &'static dyn Fn(Rc<Serializer>) -> Box<dyn Entity>;

pub struct EntityList {
entities: Vec<Option<Box<dyn Entity>>>,
entity_factory: EntityFactory,
/// Only used by read_props to avoid allocations.
field_paths: Vec<FieldPath>,
}

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()
}
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand All @@ -84,103 +105,13 @@ impl Entities {
}
Ok(())
}
}

impl std::ops::Index<usize> 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<Serializer>,
}

impl Entity {
fn new(serializer: Rc<Serializer>) -> 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<Property>, &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<FieldPath>) -> Result<()> {
fn read_props(
reader: &mut BitReader,
entity: &mut dyn Entity,
fps: &mut Vec<FieldPath>,
) -> Result<()> {
let mut fp = FieldPath::new();
fps.clear();
loop {
Expand All @@ -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())
Expand All @@ -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<str>),
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<PathNameItem>,
}

impl PathName {
fn push_field(mut self, field: Rc<str>) -> Self {
self.items.push(PathNameItem::Field(field));
self
}
impl std::ops::Index<usize> 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()
}
}

Expand All @@ -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(())
}
Expand Down
Loading

0 comments on commit 009fb12

Please sign in to comment.