1
0
Fork 0
mirror of https://codeberg.org/demostf/parser.git synced 2026-06-03 18:24:05 +02:00

strongly typed sendpropdefinition

This commit is contained in:
Robin Appelman 2021-02-13 01:25:03 +01:00
commit df9a6a138e
10 changed files with 384 additions and 206 deletions

View file

@ -14,7 +14,7 @@ use tf_demo_parser::demo::message::Message;
use tf_demo_parser::demo::packet::datatable::{ParseSendTable, SendTableName}; use tf_demo_parser::demo::packet::datatable::{ParseSendTable, SendTableName};
use tf_demo_parser::demo::packet::stringtable::StringTableEntry; use tf_demo_parser::demo::packet::stringtable::StringTableEntry;
use tf_demo_parser::demo::parser::MessageHandler; use tf_demo_parser::demo::parser::MessageHandler;
use tf_demo_parser::demo::sendprop::SendPropDefinition; use tf_demo_parser::demo::sendprop::RawSendPropDefinition;
use tf_demo_parser::{Demo, DemoParser, MatchState, MessageType, MessageTypeAnalyser, ParserState}; use tf_demo_parser::{Demo, DemoParser, MatchState, MessageType, MessageTypeAnalyser, ParserState};
struct AllMessages; struct AllMessages;

View file

@ -14,48 +14,48 @@ use tf_demo_parser::demo::message::Message;
use tf_demo_parser::demo::packet::datatable::{ParseSendTable, SendTableName}; use tf_demo_parser::demo::packet::datatable::{ParseSendTable, SendTableName};
use tf_demo_parser::demo::packet::stringtable::StringTableEntry; use tf_demo_parser::demo::packet::stringtable::StringTableEntry;
use tf_demo_parser::demo::parser::MessageHandler; use tf_demo_parser::demo::parser::MessageHandler;
use tf_demo_parser::demo::sendprop::SendPropDefinition; use tf_demo_parser::demo::sendprop::RawSendPropDefinition;
use tf_demo_parser::{Demo, DemoParser, MatchState, MessageType, MessageTypeAnalyser, ParserState}; use tf_demo_parser::{Demo, DemoParser, MatchState, MessageType, MessageTypeAnalyser, ParserState};
pub struct SendPropAnalyser; pub struct SendPropAnalyser;
impl MessageHandler for SendPropAnalyser { // impl MessageHandler for SendPropAnalyser {
type Output = Vec<ParseSendTable>; // type Output = Vec<ParseSendTable>;
//
fn does_handle(message_type: MessageType) -> bool { // fn does_handle(message_type: MessageType) -> bool {
false // false
} // }
//
fn into_output(self, state: &ParserState) -> Self::Output { // fn into_output(self, state: &ParserState) -> Self::Output {
state // state
.send_tables // .send_tables
.iter() // .iter()
.map(|v| ParseSendTable { // .map(|v| ParseSendTable {
name: v.name.clone(), // name: v.name.clone(),
props: v.props.clone(), // props: v.props.clone(),
needs_decoder: v.needs_decoder, // needs_decoder: v.needs_decoder,
}) // })
.collect() // .collect()
} // }
} // }
//
fn flatten_bench(input_file: &str, b: &mut Bencher) { // fn flatten_bench(input_file: &str, b: &mut Bencher) {
let file = fs::read(input_file).expect("Unable to read file"); // let file = fs::read(input_file).expect("Unable to read file");
let demo = Demo::new(&file); // let demo = Demo::new(&file);
let stream = demo.get_stream(); // let stream = demo.get_stream();
let (_, send_tables) = DemoParser::new_with_analyser(stream.clone(), SendPropAnalyser) // let (_, send_tables) = DemoParser::new_with_analyser(stream.clone(), SendPropAnalyser)
.parse() // .parse()
.unwrap(); // .unwrap();
b.iter(|| { // b.iter(|| {
let flat: Vec<_> = send_tables // let flat: Vec<_> = send_tables
.iter() // .iter()
.map(|table| table.flatten_props(&send_tables)) // .map(|table| table.flatten_props(&send_tables))
.collect(); // .collect();
test::black_box(flat); // test::black_box(flat);
}); // });
} // }
//
#[bench] // #[bench]
fn sendprop_test_gully(b: &mut Bencher) { // fn sendprop_test_gully(b: &mut Bencher) {
flatten_bench("data/gully.dem", b); // flatten_bench("data/gully.dem", b);
} // }

View file

@ -5,7 +5,7 @@ use serde_repr::{Deserialize_repr, Serialize_repr};
use crate::demo::message::stringtable::log_base2; use crate::demo::message::stringtable::log_base2;
use crate::demo::packet::datatable::{ClassId, SendTable}; use crate::demo::packet::datatable::{ClassId, SendTable};
use crate::demo::parser::ParseBitSkip; use crate::demo::parser::ParseBitSkip;
use crate::demo::sendprop::{SendProp, SendPropDefinition, SendPropValue}; use crate::demo::sendprop::{SendProp, SendPropIdentifier, SendPropValue};
use crate::{Parse, ParseError, ParserState, ReadResult, Result, Stream}; use crate::{Parse, ParseError, ParserState, ReadResult, Result, Stream};
use parse_display::{Display, FromStr}; use parse_display::{Display, FromStr};
use std::cmp::min; use std::cmp::min;
@ -13,7 +13,6 @@ use std::cmp::min;
use std::fmt; use std::fmt;
use std::hint::unreachable_unchecked; use std::hint::unreachable_unchecked;
use std::num::NonZeroU32; use std::num::NonZeroU32;
use std::rc::Rc;
#[derive( #[derive(
Debug, Debug,
@ -70,15 +69,15 @@ impl fmt::Display for PacketEntity {
} }
impl PacketEntity { impl PacketEntity {
fn get_prop_by_definition(&mut self, definition: &SendPropDefinition) -> Option<&mut SendProp> { fn get_prop_by_identifier(&mut self, identifier: &SendPropIdentifier) -> Option<&mut SendProp> {
self.props self.props
.iter_mut() .iter_mut()
.find(|prop| prop.definition.index == definition.index) .find(|prop| prop.identifier.index == identifier.index)
} }
pub fn apply_update(&mut self, props: Vec<SendProp>) { pub fn apply_update(&mut self, props: Vec<SendProp>) {
for prop in props { for prop in props {
match self.get_prop_by_definition(&prop.definition) { match self.get_prop_by_identifier(&prop.identifier) {
Some(existing_prop) => existing_prop.value = prop.value, Some(existing_prop) => existing_prop.value = prop.value,
None => self.props.push(prop), None => self.props.push(prop),
} }
@ -87,8 +86,8 @@ impl PacketEntity {
pub fn get_prop_by_name(&self, table_name: &str, name: &str) -> Option<&SendProp> { pub fn get_prop_by_name(&self, table_name: &str, name: &str) -> Option<&SendProp> {
self.props.iter().find(|prop| { self.props.iter().find(|prop| {
prop.definition.owner_table.as_str() == table_name prop.identifier.owner_table.as_str() == table_name
&& prop.definition.name.as_str() == name && prop.identifier.name.as_str() == name
}) })
} }
} }
@ -252,7 +251,7 @@ impl PacketEntitiesMessage {
Some(definition) => { Some(definition) => {
let value = SendPropValue::parse(stream, definition)?; let value = SendPropValue::parse(stream, definition)?;
props.push(SendProp { props.push(SendProp {
definition: Rc::clone(definition), identifier: definition.identifier(),
value, value,
}); });
} }

View file

@ -2,7 +2,8 @@ use bitbuffer::BitRead;
use crate::demo::parser::MalformedSendPropDefinitionError; use crate::demo::parser::MalformedSendPropDefinitionError;
use crate::demo::sendprop::{ use crate::demo::sendprop::{
SendPropDefinition, SendPropDefinitionIndex, SendPropFlag, SendPropName, SendPropType, RawSendPropDefinition, SendPropDefinition, SendPropDefinitionIndex, SendPropFlag, SendPropName,
SendPropType,
}; };
use crate::{Parse, ParseError, ParserState, Result, Stream}; use crate::{Parse, ParseError, ParserState, Result, Stream};
use parse_display::{Display, FromStr}; use parse_display::{Display, FromStr};
@ -50,7 +51,18 @@ pub struct ServerClass {
} }
#[derive( #[derive(
BitRead, PartialEq, Eq, Hash, Debug, Serialize, Deserialize, Clone, Display, PartialOrd, Ord, BitRead,
PartialEq,
Eq,
Hash,
Debug,
Serialize,
Deserialize,
Clone,
Display,
PartialOrd,
Ord,
Default,
)] )]
pub struct SendTableName(Rc<String>); pub struct SendTableName(Rc<String>);
@ -69,7 +81,7 @@ impl From<String> for SendTableName {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ParseSendTable { pub struct ParseSendTable {
pub name: SendTableName, pub name: SendTableName,
pub props: Vec<Rc<SendPropDefinition>>, pub props: Vec<Rc<RawSendPropDefinition>>,
pub needs_decoder: bool, pub needs_decoder: bool,
} }
@ -85,8 +97,8 @@ impl ParseSendTable {
for prop_index in 0..prop_count { for prop_index in 0..prop_count {
let definition_index = SendPropDefinitionIndex::new(table_index, prop_index); let definition_index = SendPropDefinitionIndex::new(table_index, prop_index);
let prop: SendPropDefinition = let prop: RawSendPropDefinition =
SendPropDefinition::read(stream, name.clone(), definition_index)?; RawSendPropDefinition::read(stream, name.clone(), definition_index)?;
if prop.flags.contains(SendPropFlag::InsideArray) { if prop.flags.contains(SendPropFlag::InsideArray) {
if array_element_prop.is_some() || prop.flags.contains(SendPropFlag::ChangesOften) { if array_element_prop.is_some() || prop.flags.contains(SendPropFlag::ChangesOften) {
return Err(MalformedSendPropDefinitionError::ArrayChangesOften.into()); return Err(MalformedSendPropDefinitionError::ArrayChangesOften.into());
@ -112,7 +124,7 @@ impl ParseSendTable {
} }
impl ParseSendTable { impl ParseSendTable {
pub fn flatten_props(&self, tables: &[ParseSendTable]) -> Vec<Rc<SendPropDefinition>> { pub fn flatten_props(&self, tables: &[ParseSendTable]) -> Vec<Rc<RawSendPropDefinition>> {
let mut flat = Vec::with_capacity(32); let mut flat = Vec::with_capacity(32);
self.get_all_props(tables, &self.get_excludes(tables), &mut flat); self.get_all_props(tables, &self.get_excludes(tables), &mut flat);
@ -137,7 +149,7 @@ impl ParseSendTable {
if let Some(exclude_table) = prop.get_exclude_table() { if let Some(exclude_table) = prop.get_exclude_table() {
excludes.push(Exclude { excludes.push(Exclude {
table: exclude_table, table: exclude_table,
prop: &prop.name, prop: &prop.identifier.name,
}) })
} else if let Some(table) = prop.get_data_table(tables) { } else if let Some(table) = prop.get_data_table(tables) {
excludes.extend_from_slice(&table.get_excludes(tables)); excludes.extend_from_slice(&table.get_excludes(tables));
@ -152,7 +164,7 @@ impl ParseSendTable {
&self, &self,
tables: &[ParseSendTable], tables: &[ParseSendTable],
excludes: &[Exclude], excludes: &[Exclude],
props: &mut Vec<Rc<SendPropDefinition>>, props: &mut Vec<Rc<RawSendPropDefinition>>,
) { ) {
let mut local_props = Vec::new(); let mut local_props = Vec::new();
@ -164,8 +176,8 @@ impl ParseSendTable {
&self, &self,
tables: &[ParseSendTable], tables: &[ParseSendTable],
excludes: &[Exclude], excludes: &[Exclude],
local_props: &mut Vec<Rc<SendPropDefinition>>, local_props: &mut Vec<Rc<RawSendPropDefinition>>,
props: &mut Vec<Rc<SendPropDefinition>>, props: &mut Vec<Rc<RawSendPropDefinition>>,
) { ) {
self.props self.props
.iter() .iter()
@ -192,17 +204,16 @@ struct Exclude<'a> {
} }
impl<'a> Exclude<'a> { impl<'a> Exclude<'a> {
pub fn matches(&self, prop: &SendPropDefinition) -> bool { pub fn matches(&self, prop: &RawSendPropDefinition) -> bool {
self.table == &prop.owner_table && *self.prop == prop.name self.table == &prop.identifier.owner_table && *self.prop == prop.identifier.name
} }
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct SendTable { pub struct SendTable {
pub name: SendTableName, pub name: SendTableName,
pub props: Vec<Rc<SendPropDefinition>>,
pub needs_decoder: bool, pub needs_decoder: bool,
pub flattened_props: Vec<Rc<SendPropDefinition>>, pub flattened_props: Vec<SendPropDefinition>,
} }
#[derive(Debug)] #[derive(Debug)]

View file

@ -206,7 +206,7 @@ impl GameStateAnalyser {
pub fn handle_player_resource(&mut self, entity: &PacketEntity) { pub fn handle_player_resource(&mut self, entity: &PacketEntity) {
for prop in &entity.props { for prop in &entity.props {
if let Ok(player_id) = u32::from_str(prop.definition.name.as_str()) { if let Ok(player_id) = u32::from_str(prop.identifier.name.as_str()) {
let entity_id = EntityId::from(player_id); let entity_id = EntityId::from(player_id);
if let Some(player) = self if let Some(player) = self
.state .state
@ -214,7 +214,7 @@ impl GameStateAnalyser {
.iter_mut() .iter_mut()
.find(|player| player.entity == entity_id) .find(|player| player.entity == entity_id)
{ {
match prop.definition.owner_table.as_str() { match prop.identifier.owner_table.as_str() {
"m_iTeam" => { "m_iTeam" => {
player.team = Team::new(i64::try_from(&prop.value).unwrap_or_default()) player.team = Team::new(i64::try_from(&prop.value).unwrap_or_default())
} }
@ -237,8 +237,8 @@ impl GameStateAnalyser {
let player = self.state.get_or_create_player(entity.entity_index); let player = self.state.get_or_create_player(entity.entity_index);
for prop in &entity.props { for prop in &entity.props {
match prop.definition.owner_table.as_str() { match prop.identifier.owner_table.as_str() {
"DT_BasePlayer" => match prop.definition.name.as_str() { "DT_BasePlayer" => match prop.identifier.name.as_str() {
"m_iHealth" => { "m_iHealth" => {
player.health = i64::try_from(&prop.value).unwrap_or_default() as u16 player.health = i64::try_from(&prop.value).unwrap_or_default() as u16
} }
@ -252,7 +252,7 @@ impl GameStateAnalyser {
_ => {} _ => {}
}, },
"DT_TFLocalPlayerExclusive" | "DT_TFNonLocalPlayerExclusive" => { "DT_TFLocalPlayerExclusive" | "DT_TFNonLocalPlayerExclusive" => {
match prop.definition.name.as_str() { match prop.identifier.name.as_str() {
"m_vecOrigin" => { "m_vecOrigin" => {
let pos_xy = VectorXY::try_from(&prop.value).unwrap_or_default(); let pos_xy = VectorXY::try_from(&prop.value).unwrap_or_default();
player.position.x = pos_xy.x; player.position.x = pos_xy.x;

View file

@ -71,7 +71,7 @@ impl<'a, T: MessageHandler> DemoHandler<'a, T> {
pub fn handle_packet(&mut self, packet: Packet<'a>) -> Result<()> { pub fn handle_packet(&mut self, packet: Packet<'a>) -> Result<()> {
match packet { match packet {
Packet::DataTables(packet) => { Packet::DataTables(packet) => {
self.handle_data_table(packet.tables, packet.server_classes); self.handle_data_table(packet.tables, packet.server_classes)?;
} }
Packet::StringTables(packet) => { Packet::StringTables(packet) => {
for table in packet.tables.into_iter() { for table in packet.tables.into_iter() {
@ -127,11 +127,11 @@ impl<'a, T: MessageHandler> DemoHandler<'a, T> {
&mut self, &mut self,
send_tables: Vec<ParseSendTable>, send_tables: Vec<ParseSendTable>,
server_classes: Vec<ServerClass>, server_classes: Vec<ServerClass>,
) { ) -> Result<()> {
self.analyser self.analyser
.handle_data_tables(&send_tables, &server_classes); .handle_data_tables(&send_tables, &server_classes);
self.state_handler self.state_handler
.handle_data_table(send_tables, server_classes); .handle_data_table(send_tables, server_classes)
} }
fn handle_message(&mut self, message: Message<'a>) { fn handle_message(&mut self, message: Message<'a>) {

View file

@ -11,10 +11,11 @@ use crate::demo::packet::datatable::{
}; };
use crate::demo::packet::stringtable::StringTableEntry; use crate::demo::packet::stringtable::StringTableEntry;
use crate::demo::sendprop::SendProp; use crate::demo::sendprop::{SendProp, SendPropDefinition};
use crate::nullhasher::NullHasherBuilder; use crate::nullhasher::NullHasherBuilder;
use crate::{Result, Stream}; use crate::{Result, Stream};
use std::cell::RefCell; use std::cell::RefCell;
use std::convert::TryFrom;
#[derive(Default, Clone)] #[derive(Default, Clone)]
pub struct DemoMeta { pub struct DemoMeta {
@ -100,7 +101,7 @@ impl<'a> ParserState {
&mut self, &mut self,
parse_tables: Vec<ParseSendTable>, parse_tables: Vec<ParseSendTable>,
server_classes: Vec<ServerClass>, server_classes: Vec<ServerClass>,
) { ) -> Result<()> {
if self.handle_entities { if self.handle_entities {
let flat_props: Vec<_> = parse_tables let flat_props: Vec<_> = parse_tables
.iter() .iter()
@ -111,17 +112,19 @@ impl<'a> ParserState {
.into_iter() .into_iter()
.zip(flat_props.into_iter()) .zip(flat_props.into_iter())
.map(|(parse_table, flat)| { .map(|(parse_table, flat)| {
( Ok((
parse_table.name.clone(), parse_table.name.clone(),
SendTable { SendTable {
name: parse_table.name, name: parse_table.name,
props: parse_table.props,
needs_decoder: parse_table.needs_decoder, needs_decoder: parse_table.needs_decoder,
flattened_props: flat, flattened_props: flat
.into_iter()
.map(|raw| SendPropDefinition::try_from(raw.as_ref()))
.collect::<std::result::Result<Vec<_>, _>>()?,
}, },
) ))
}) })
.collect(); .collect::<Result<_>>()?;
self.server_classes = server_classes; self.server_classes = server_classes;
@ -133,6 +136,8 @@ impl<'a> ParserState {
} }
} }
} }
Ok(())
} }
pub fn handle_string_table_meta(&mut self, table: StringTableMeta) { pub fn handle_string_table_meta(&mut self, table: StringTableMeta) {

View file

@ -40,34 +40,32 @@ impl From<String> for SendPropName {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct SendPropDefinition { pub struct RawSendPropDefinition {
pub prop_type: SendPropType, pub prop_type: SendPropType,
pub name: SendPropName, pub identifier: Rc<SendPropIdentifier>,
pub flags: SendPropFlags, pub flags: SendPropFlags,
pub table_name: Option<SendTableName>, pub table_name: Option<SendTableName>,
pub owner_table: SendTableName,
pub low_value: Option<f32>, pub low_value: Option<f32>,
pub high_value: Option<f32>, pub high_value: Option<f32>,
pub bit_count: Option<u32>, pub bit_count: Option<u32>,
pub element_count: Option<u16>, pub element_count: Option<u16>,
pub array_property: Option<Box<SendPropDefinition>>, pub array_property: Option<Box<RawSendPropDefinition>>,
pub index: SendPropDefinitionIndex,
} }
impl PartialEq for SendPropDefinition { impl PartialEq for RawSendPropDefinition {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.index == other.index self.identifier.index == other.identifier.index
} }
} }
impl fmt::Display for SendPropDefinition { impl fmt::Display for RawSendPropDefinition {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.prop_type { match self.prop_type {
SendPropType::Vector | SendPropType::VectorXY => write!( SendPropType::Vector | SendPropType::VectorXY => write!(
f, f,
"{}::{}({})(flags: {}, low: {}, high: {}, bits: {})", "{}::{}({})(flags: {}, low: {}, high: {}, bits: {})",
self.owner_table, self.identifier.owner_table,
self.name, self.identifier.name,
self.prop_type, self.prop_type,
self.flags, self.flags,
self.low_value.unwrap_or_default(), self.low_value.unwrap_or_default(),
@ -77,8 +75,8 @@ impl fmt::Display for SendPropDefinition {
SendPropType::Float => write!( SendPropType::Float => write!(
f, f,
"{}::{}({})(flags: {}, low: {}, high: {}, bits: {})", "{}::{}({})(flags: {}, low: {}, high: {}, bits: {})",
self.owner_table, self.identifier.owner_table,
self.name, self.identifier.name,
self.prop_type, self.prop_type,
self.flags, self.flags,
self.low_value.unwrap_or_default(), self.low_value.unwrap_or_default(),
@ -88,47 +86,55 @@ impl fmt::Display for SendPropDefinition {
SendPropType::Int => write!( SendPropType::Int => write!(
f, f,
"{}::{}({})(flags: {}, bits: {})", "{}::{}({})(flags: {}, bits: {})",
self.owner_table, self.identifier.owner_table,
self.name, self.identifier.name,
self.prop_type, self.prop_type,
self.flags, self.flags,
self.bit_count.unwrap_or(32) self.bit_count.unwrap_or(32)
), ),
SendPropType::String => { SendPropType::String => {
write!(f, "{}::{}({})", self.owner_table, self.name, self.prop_type) write!(
f,
"{}::{}({})",
self.identifier.owner_table, self.identifier.name, self.prop_type
)
} }
SendPropType::Array => match &self.array_property { SendPropType::Array => match &self.array_property {
Some(array_prop) => write!( Some(array_prop) => write!(
f, f,
"{}::{}([{}({})] * {})", "{}::{}([{}({})] * {})",
self.owner_table, self.identifier.owner_table,
self.name, self.identifier.name,
array_prop.prop_type, array_prop.prop_type,
array_prop.flags, array_prop.flags,
self.element_count.unwrap_or_default(), self.element_count.unwrap_or_default(),
), ),
None => write!(f, "{}(Malformed array)", self.name), None => write!(f, "{}(Malformed array)", self.identifier.name),
}, },
SendPropType::DataTable => match &self.table_name { SendPropType::DataTable => match &self.table_name {
Some(sub_table) => write!( Some(sub_table) => write!(
f, f,
"{}::{}(DataTable = {})", "{}::{}(DataTable = {})",
self.owner_table, self.name, sub_table self.identifier.owner_table, self.identifier.name, sub_table
), ),
None => write!(f, "{}(Malformed DataTable)", self.name), None => write!(f, "{}(Malformed DataTable)", self.identifier.name),
}, },
SendPropType::NumSendPropTypes => { SendPropType::NumSendPropTypes => {
write!(f, "{}::{}(NumSendPropTypes)", self.owner_table, self.name) write!(
f,
"{}::{}(NumSendPropTypes)",
self.identifier.owner_table, self.identifier.name
)
} }
} }
} }
} }
impl SendPropDefinition { impl RawSendPropDefinition {
pub fn with_array_property(self, array_property: Self) -> Self { pub fn with_array_property(self, array_property: Self) -> Self {
SendPropDefinition { RawSendPropDefinition {
prop_type: self.prop_type, prop_type: self.prop_type,
name: self.name, identifier: self.identifier,
flags: self.flags, flags: self.flags,
table_name: self.table_name, table_name: self.table_name,
low_value: self.low_value, low_value: self.low_value,
@ -136,8 +142,6 @@ impl SendPropDefinition {
bit_count: self.bit_count, bit_count: self.bit_count,
element_count: self.element_count, element_count: self.element_count,
array_property: Some(Box::new(array_property)), array_property: Some(Box::new(array_property)),
owner_table: self.owner_table,
index: self.index,
} }
} }
@ -187,9 +191,13 @@ impl SendPropDefinition {
} }
} }
Ok(SendPropDefinition { Ok(RawSendPropDefinition {
prop_type, prop_type,
identifier: Rc::new(SendPropIdentifier {
index,
name, name,
owner_table,
}),
flags, flags,
table_name, table_name,
low_value, low_value,
@ -197,8 +205,6 @@ impl SendPropDefinition {
bit_count, bit_count,
element_count, element_count,
array_property: None, array_property: None,
owner_table,
index,
}) })
} }
@ -304,6 +310,173 @@ impl BitRead<'_, LittleEndian> for SendPropFlags {
} }
} }
#[derive(Debug, Clone)]
pub enum FloatDefinition {
Coord,
CoordMP,
CoordMPLowPrecision,
CoordMPIntegral,
FloatNoScale,
NormalVarFloat,
Scaled { bit_count: u8, high: f32, low: f32 },
}
impl FloatDefinition {
pub fn new(
flags: SendPropFlags,
bit_count: Option<u32>,
high: Option<f32>,
low: Option<f32>,
) -> std::result::Result<Self, MalformedSendPropDefinitionError> {
if flags.contains(SendPropFlag::Coord) {
Ok(FloatDefinition::Coord)
} else if flags.contains(SendPropFlag::CoordMP) {
Ok(FloatDefinition::CoordMP)
} else if flags.contains(SendPropFlag::CoordMPLowPrecision) {
Ok(FloatDefinition::CoordMPLowPrecision)
} else if flags.contains(SendPropFlag::CoordMPIntegral) {
Ok(FloatDefinition::CoordMPIntegral)
} else if flags.contains(SendPropFlag::NoScale) {
Ok(FloatDefinition::FloatNoScale)
} else if flags.contains(SendPropFlag::NormalVarInt) {
Ok(FloatDefinition::NormalVarFloat)
} else if let (Some(bit_count), Some(high), Some(low)) = (bit_count, high, low) {
Ok(FloatDefinition::Scaled {
bit_count: bit_count as u8,
high,
low,
})
} else {
Err(MalformedSendPropDefinitionError::UnsizedFloat)
}
}
}
#[derive(Debug, Clone)]
pub enum SendPropDefinition {
NormalVarInt {
identifier: Rc<SendPropIdentifier>,
unsigned: bool,
},
UnsignedInt {
identifier: Rc<SendPropIdentifier>,
bit_count: u8,
},
Int {
identifier: Rc<SendPropIdentifier>,
bit_count: u8,
},
Float {
identifier: Rc<SendPropIdentifier>,
definition: FloatDefinition,
},
String {
identifier: Rc<SendPropIdentifier>,
},
Vector {
identifier: Rc<SendPropIdentifier>,
definition: FloatDefinition,
},
VectorXY {
identifier: Rc<SendPropIdentifier>,
definition: FloatDefinition,
},
Array {
identifier: Rc<SendPropIdentifier>,
inner_definition: Box<SendPropDefinition>,
count_bit_count: u16,
},
}
impl SendPropDefinition {
pub fn identifier(&self) -> Rc<SendPropIdentifier> {
match self {
SendPropDefinition::NormalVarInt { identifier, .. } => identifier.clone(),
SendPropDefinition::UnsignedInt { identifier, .. } => identifier.clone(),
SendPropDefinition::Int { identifier, .. } => identifier.clone(),
SendPropDefinition::Float { identifier, .. } => identifier.clone(),
SendPropDefinition::String { identifier, .. } => identifier.clone(),
SendPropDefinition::Vector { identifier, .. } => identifier.clone(),
SendPropDefinition::VectorXY { identifier, .. } => identifier.clone(),
SendPropDefinition::Array { identifier, .. } => identifier.clone(),
}
}
}
impl TryFrom<&RawSendPropDefinition> for SendPropDefinition {
type Error = MalformedSendPropDefinitionError;
fn try_from(definition: &RawSendPropDefinition) -> std::result::Result<Self, Self::Error> {
let identifier = definition.identifier.clone();
match definition.prop_type {
SendPropType::Int => {
if definition.flags.contains(SendPropFlag::NormalVarInt) {
Ok(SendPropDefinition::NormalVarInt {
identifier,
unsigned: definition.flags.contains(SendPropFlag::Unsigned),
})
} else if definition.flags.contains(SendPropFlag::Unsigned) {
Ok(SendPropDefinition::UnsignedInt {
identifier,
bit_count: definition.bit_count.unwrap_or(32) as u8,
})
} else {
Ok(SendPropDefinition::Int {
identifier,
bit_count: definition.bit_count.unwrap_or(32) as u8,
})
}
}
SendPropType::Float => Ok(SendPropDefinition::Float {
identifier,
definition: FloatDefinition::new(
definition.flags,
definition.bit_count,
definition.high_value,
definition.low_value,
)?,
}),
SendPropType::String => Ok(SendPropDefinition::String { identifier }),
SendPropType::Vector => Ok(SendPropDefinition::Vector {
identifier,
definition: FloatDefinition::new(
definition.flags,
definition.bit_count,
definition.high_value,
definition.low_value,
)?,
}),
SendPropType::VectorXY => Ok(SendPropDefinition::VectorXY {
identifier,
definition: FloatDefinition::new(
definition.flags,
definition.bit_count,
definition.high_value,
definition.low_value,
)?,
}),
SendPropType::Array => {
let count_bit_count = log_base2(
definition
.element_count
.ok_or(MalformedSendPropDefinitionError::UnsizedArray)?,
) as u16
+ 1;
let child_definition = definition
.array_property
.as_deref()
.ok_or(MalformedSendPropDefinitionError::UntypedArray)?;
Ok(SendPropDefinition::Array {
identifier,
inner_definition: Box::new(SendPropDefinition::try_from(child_definition)?),
count_bit_count,
})
}
_ => Err(MalformedSendPropDefinitionError::InvalidPropType),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)] #[serde(untagged)]
pub enum SendPropValue { pub enum SendPropValue {
@ -385,108 +558,90 @@ impl fmt::Display for SendPropValue {
impl SendPropValue { impl SendPropValue {
pub fn parse(stream: &mut Stream, definition: &SendPropDefinition) -> Result<Self> { pub fn parse(stream: &mut Stream, definition: &SendPropDefinition) -> Result<Self> {
match definition.prop_type { match definition {
SendPropType::Int => Self::read_int(stream, definition).map(SendPropValue::from), SendPropDefinition::NormalVarInt { unsigned, .. } => read_var_int(stream, !*unsigned)
SendPropType::Float => Self::read_float(stream, definition).map(SendPropValue::from),
SendPropType::String => Self::read_string(stream, definition).map(SendPropValue::from),
SendPropType::Vector => Self::read_vector(stream, definition).map(SendPropValue::from),
SendPropType::VectorXY => {
Self::read_vector_xy(stream, definition).map(SendPropValue::from)
}
SendPropType::Array => Self::read_array(stream, definition).map(SendPropValue::from),
_ => Err(MalformedSendPropDefinitionError::InvalidPropType.into()),
}
}
fn read_int(stream: &mut Stream, definition: &SendPropDefinition) -> Result<i64> {
if definition.flags.contains(SendPropFlag::NormalVarInt) {
read_var_int(stream, !definition.flags.contains(SendPropFlag::Unsigned))
.map_err(ParseError::from) .map_err(ParseError::from)
.map(|int| int as i64) .map(|int| int as i64)
} else if definition.flags.contains(SendPropFlag::Unsigned) { .map(SendPropValue::from),
let unsigned: u32 = stream.read_sized(definition.bit_count.unwrap_or(32) as usize)?; SendPropDefinition::UnsignedInt { bit_count, .. } => {
Ok(unsigned as i64) Ok((stream.read_sized::<u32>(*bit_count as usize)? as i64).into())
} else { }
stream SendPropDefinition::Int { bit_count, .. } => stream
.read_int(definition.bit_count.unwrap_or(32) as usize) .read_int::<i32>((*bit_count) as usize)
.map_err(ParseError::from) .map_err(ParseError::from)
.map(SendPropValue::from),
SendPropDefinition::Float {
definition: float_definition,
..
} => Self::read_float(stream, float_definition).map(SendPropValue::from),
SendPropDefinition::String { .. } => {
let length = stream.read_int(9)?;
stream
.read_sized::<String>(length)
.map_err(ParseError::from)
.map(SendPropValue::from)
} }
SendPropDefinition::Vector {
definition: float_definition,
..
} => Ok(Vector {
x: Self::read_float(stream, float_definition)?,
y: Self::read_float(stream, float_definition)?,
z: Self::read_float(stream, float_definition)?,
} }
.into()),
fn read_array( SendPropDefinition::VectorXY {
stream: &mut Stream, definition: float_definition,
definition: &SendPropDefinition, ..
) -> Result<Vec<SendPropValue>> { } => Ok(VectorXY {
let num_bits = log_base2( x: Self::read_float(stream, float_definition)?,
definition y: Self::read_float(stream, float_definition)?,
.element_count }
.ok_or(MalformedSendPropDefinitionError::UnsizedArray)?, .into()),
) + 1; SendPropDefinition::Array {
count_bit_count,
let count = stream.read_int(num_bits as usize)?; inner_definition,
..
} => {
let count = stream.read_int(*count_bit_count as usize)?;
let mut values = Vec::with_capacity(min(count, 128)); let mut values = Vec::with_capacity(min(count, 128));
let child_definition = definition
.array_property
.as_ref()
.ok_or(MalformedSendPropDefinitionError::UntypedArray)?;
for _ in 0..count { for _ in 0..count {
values.push(Self::parse(stream, child_definition)?); values.push(Self::parse(stream, inner_definition)?);
} }
Ok(values) Ok(values.into())
}
}
} }
fn read_string(stream: &mut Stream, _definition: &SendPropDefinition) -> Result<String> { fn read_float(stream: &mut Stream, definition: &FloatDefinition) -> Result<f32> {
let length = stream.read_int(9)?; match definition {
stream.read_sized(length).map_err(ParseError::from) FloatDefinition::Coord => read_bit_coord(stream).map_err(ParseError::from),
} FloatDefinition::CoordMP => {
fn read_vector(stream: &mut Stream, definition: &SendPropDefinition) -> Result<Vector> {
Ok(Vector {
x: Self::read_float(stream, definition)?,
y: Self::read_float(stream, definition)?,
z: Self::read_float(stream, definition)?,
})
}
fn read_vector_xy(stream: &mut Stream, definition: &SendPropDefinition) -> Result<VectorXY> {
Ok(VectorXY {
x: Self::read_float(stream, definition)?,
y: Self::read_float(stream, definition)?,
})
}
fn read_float(stream: &mut Stream, definition: &SendPropDefinition) -> Result<f32> {
if definition.flags.contains(SendPropFlag::Coord) {
read_bit_coord(stream).map_err(ParseError::from)
} else if definition.flags.contains(SendPropFlag::CoordMP) {
read_bit_coord_mp(stream, false, false).map_err(ParseError::from) read_bit_coord_mp(stream, false, false).map_err(ParseError::from)
} else if definition.flags.contains(SendPropFlag::CoordMPLowPrecision) { }
FloatDefinition::CoordMPLowPrecision => {
read_bit_coord_mp(stream, false, true).map_err(ParseError::from) read_bit_coord_mp(stream, false, true).map_err(ParseError::from)
} else if definition.flags.contains(SendPropFlag::CoordMPIntegral) { }
FloatDefinition::CoordMPIntegral => {
read_bit_coord_mp(stream, true, false).map_err(ParseError::from) read_bit_coord_mp(stream, true, false).map_err(ParseError::from)
} else if definition.flags.contains(SendPropFlag::NoScale) { }
stream.read().map_err(ParseError::from) FloatDefinition::FloatNoScale => stream.read().map_err(ParseError::from),
} else if definition.flags.contains(SendPropFlag::NormalVarInt) { FloatDefinition::NormalVarFloat => read_bit_normal(stream).map_err(ParseError::from),
read_bit_normal(stream).map_err(ParseError::from) FloatDefinition::Scaled {
} else { bit_count,
let bit_count = definition low,
.bit_count high,
.ok_or(MalformedSendPropDefinitionError::UnsizedFloat)?; } => {
let high = definition let raw: u32 = stream.read_int(*bit_count as usize)?;
.high_value
.ok_or(MalformedSendPropDefinitionError::UnsizedFloat)?;
let low = definition
.low_value
.ok_or(MalformedSendPropDefinitionError::UnsizedFloat)?;
let raw: u32 = stream.read_int(bit_count as usize)?;
// is this -1 correct?, it is consistent with the js version but seems weird // is this -1 correct?, it is consistent with the js version but seems weird
let percentage = (raw as f32) / ((1i32.wrapping_shl(bit_count)) as f32 - 1.0); let percentage =
(raw as f32) / ((1i32.wrapping_shl(*bit_count as u32)) as f32 - 1.0);
Ok(low + ((high - low) * percentage)) Ok(low + ((high - low) * percentage))
} }
} }
}
} }
impl From<i32> for SendPropValue { impl From<i32> for SendPropValue {
@ -601,10 +756,18 @@ impl SendPropDefinitionIndex {
} }
} }
#[derive(Debug, Display)]
#[display("{owner_table}::{name}")]
pub struct SendPropIdentifier {
pub index: SendPropDefinitionIndex,
pub name: SendPropName,
pub owner_table: SendTableName,
}
#[derive(Debug, Clone, Display)] #[derive(Debug, Clone, Display)]
#[display("{definition.owner_table}::{definition.name} = {value}")] #[display("{identifier} = {value}")]
pub struct SendProp { pub struct SendProp {
pub definition: Rc<SendPropDefinition>, pub identifier: Rc<SendPropIdentifier>,
pub value: SendPropValue, pub value: SendPropValue,
} }

View file

@ -54,7 +54,7 @@ impl EntityDump {
.into_iter() .into_iter()
.map(|prop| { .map(|prop| {
( (
format!("{}.{}", prop.definition.owner_table, prop.definition.name), format!("{}.{}", prop.identifier.owner_table, prop.identifier.name),
prop.value, prop.value,
) )
}) })

View file

@ -48,7 +48,7 @@ fn flatten_test(input_file: &str, snapshot_file: &str) {
table table
.flatten_props(&send_tables) .flatten_props(&send_tables)
.into_iter() .into_iter()
.map(|prop| format!("{}.{}", prop.owner_table, prop.name)) .map(|prop| format!("{}.{}", prop.identifier.owner_table, prop.identifier.name))
.collect(), .collect(),
) )
}) })