mirror of
https://codeberg.org/demostf/parser.git
synced 2026-06-03 10:14:06 +02:00
rework baseline prop handling
This commit is contained in:
parent
74ad7d121f
commit
bae9acdd92
9 changed files with 135 additions and 105 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -1120,6 +1120,7 @@ dependencies = [
|
||||||
"err-derive",
|
"err-derive",
|
||||||
"fnv",
|
"fnv",
|
||||||
"iai",
|
"iai",
|
||||||
|
"itertools",
|
||||||
"jemallocator",
|
"jemallocator",
|
||||||
"main_error",
|
"main_error",
|
||||||
"no-panic",
|
"no-panic",
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ steamid-ng = "1.0.0"
|
||||||
schemars = { version = "0.8.10", optional = true }
|
schemars = { version = "0.8.10", optional = true }
|
||||||
tracing = { version = "0.1.36", optional = true }
|
tracing = { version = "0.1.36", optional = true }
|
||||||
tracing-subscriber = { version = "0.3.15", features = ["env-filter"], optional = true }
|
tracing-subscriber = { version = "0.3.15", features = ["env-filter"], optional = true }
|
||||||
|
itertools = "0.10.3"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
schema = ["schemars", "bitbuffer/schemars"]
|
schema = ["schemars", "bitbuffer/schemars"]
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ impl MessageHandler for PropAnalyzer {
|
||||||
matches!(message_type, MessageType::PacketEntities)
|
matches!(message_type, MessageType::PacketEntities)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_message(&mut self, message: &Message, _tick: u32) {
|
fn handle_message(&mut self, message: &Message, _tick: u32, _parser_state: &ParserState) {
|
||||||
if let Message::PacketEntities(message) = message {
|
if let Message::PacketEntities(message) = message {
|
||||||
for entity in &message.entities {
|
for entity in &message.entities {
|
||||||
for prop in &entity.props {
|
for prop in &entity.props {
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,12 @@ pub enum MaybeUtf8String {
|
||||||
Invalid(Vec<u8>),
|
Invalid(Vec<u8>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<&'_ str> for MaybeUtf8String {
|
||||||
|
fn from(str: &'_ str) -> Self {
|
||||||
|
MaybeUtf8String::Valid(str.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for MaybeUtf8String {
|
impl Default for MaybeUtf8String {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
MaybeUtf8String::Valid(String::new())
|
MaybeUtf8String::Valid(String::new())
|
||||||
|
|
@ -62,9 +68,15 @@ impl<'a, E: Endianness> BitRead<'a, E> for MaybeUtf8String {
|
||||||
match String::read(stream) {
|
match String::read(stream) {
|
||||||
Ok(str) => Ok(MaybeUtf8String::Valid(str)),
|
Ok(str) => Ok(MaybeUtf8String::Valid(str)),
|
||||||
Err(bitbuffer::BitError::Utf8Error(_, size)) => {
|
Err(bitbuffer::BitError::Utf8Error(_, size)) => {
|
||||||
stream.set_pos(stream.pos() - size * 8)?;
|
stream.set_pos(stream.pos().saturating_sub(size * 8))?;
|
||||||
let data = stream.read_sized(size)?;
|
let mut data: Vec<u8> = stream.read_sized(size)?;
|
||||||
Ok(MaybeUtf8String::Invalid(data))
|
while data.last() == Some(&0) {
|
||||||
|
data.pop();
|
||||||
|
}
|
||||||
|
match String::from_utf8(data) {
|
||||||
|
Ok(str) => Ok(MaybeUtf8String::Valid(str)),
|
||||||
|
Err(e) => Ok(MaybeUtf8String::Invalid(e.into_bytes())),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use bitbuffer::{BitRead, BitReadSized, BitWrite, BitWriteSized, BitWriteStream, LittleEndian};
|
use bitbuffer::{BitRead, BitReadSized, BitWrite, BitWriteSized, BitWriteStream, LittleEndian};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_repr::{Deserialize_repr, Serialize_repr};
|
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
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};
|
||||||
|
|
@ -10,6 +11,7 @@ use crate::{Parse, ParseError, ParserState, ReadResult, Result, Stream};
|
||||||
use parse_display::{Display, FromStr};
|
use parse_display::{Display, FromStr};
|
||||||
use std::cmp::{min, Ordering};
|
use std::cmp::{min, Ordering};
|
||||||
|
|
||||||
|
use itertools::Either;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::num::NonZeroU32;
|
use std::num::NonZeroU32;
|
||||||
#[cfg(feature = "trace")]
|
#[cfg(feature = "trace")]
|
||||||
|
|
@ -87,12 +89,13 @@ pub enum UpdateType {
|
||||||
pub struct PacketEntity {
|
pub struct PacketEntity {
|
||||||
pub server_class: ClassId,
|
pub server_class: ClassId,
|
||||||
pub entity_index: EntityId,
|
pub entity_index: EntityId,
|
||||||
pub baseline_props: Vec<SendProp>,
|
|
||||||
pub props: Vec<SendProp>,
|
pub props: Vec<SendProp>,
|
||||||
pub in_pvs: bool,
|
pub in_pvs: bool,
|
||||||
pub update_type: UpdateType,
|
pub update_type: UpdateType,
|
||||||
pub serial_number: u32,
|
pub serial_number: u32,
|
||||||
pub delay: Option<f32>,
|
pub delay: Option<f32>,
|
||||||
|
pub delta: Option<u32>,
|
||||||
|
pub baseline_index: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for PacketEntity {
|
impl fmt::Display for PacketEntity {
|
||||||
|
|
@ -110,8 +113,13 @@ impl PacketEntity {
|
||||||
self.props.iter_mut().find(|prop| prop.identifier == *index)
|
self.props.iter_mut().find(|prop| prop.identifier == *index)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_prop_by_identifier(&self, index: &SendPropIdentifier) -> Option<&SendProp> {
|
pub fn get_prop_by_identifier(
|
||||||
self.props().find(|prop| prop.identifier == *index)
|
&self,
|
||||||
|
index: &SendPropIdentifier,
|
||||||
|
parser_state: &ParserState,
|
||||||
|
) -> Option<SendProp> {
|
||||||
|
self.props(parser_state)
|
||||||
|
.find(|prop| prop.identifier == *index)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply_update(&mut self, props: &[SendProp]) {
|
pub fn apply_update(&mut self, props: &[SendProp]) {
|
||||||
|
|
@ -123,19 +131,42 @@ 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,
|
||||||
|
parser_state: &ParserState,
|
||||||
|
) -> Option<SendProp> {
|
||||||
let identifier = SendPropIdentifier::new(table_name, name);
|
let identifier = SendPropIdentifier::new(table_name, name);
|
||||||
self.get_prop_by_identifier(&identifier)
|
self.get_prop_by_identifier(&identifier, parser_state)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn props(&self) -> impl Iterator<Item = &SendProp> {
|
fn get_baseline_props<'a>(&self, parser_state: &'a ParserState) -> Cow<'a, [SendProp]> {
|
||||||
self.baseline_props.iter().chain(self.props.iter())
|
parser_state
|
||||||
|
.get_baseline(
|
||||||
|
self.baseline_index,
|
||||||
|
self.entity_index,
|
||||||
|
self.server_class,
|
||||||
|
&parser_state.send_tables[usize::from(self.server_class)],
|
||||||
|
self.delta.is_some(),
|
||||||
|
)
|
||||||
|
.unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_props(self) -> impl Iterator<Item = SendProp> {
|
pub fn props<'a>(
|
||||||
self.baseline_props
|
&'a self,
|
||||||
.into_iter()
|
parser_state: &'a ParserState,
|
||||||
.chain(self.props.into_iter())
|
) -> impl Iterator<Item = SendProp> + 'a {
|
||||||
|
if self.update_type == UpdateType::Enter {
|
||||||
|
Either::Left(
|
||||||
|
self.get_baseline_props(parser_state)
|
||||||
|
.into_owned()
|
||||||
|
.into_iter()
|
||||||
|
.chain(self.props.iter().cloned()),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Either::Right(self.props.iter().cloned())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -230,12 +261,13 @@ fn get_entity_for_update(
|
||||||
Ok(PacketEntity {
|
Ok(PacketEntity {
|
||||||
server_class: class_id,
|
server_class: class_id,
|
||||||
entity_index,
|
entity_index,
|
||||||
baseline_props: vec![],
|
|
||||||
props: Vec::with_capacity(8),
|
props: Vec::with_capacity(8),
|
||||||
in_pvs: false,
|
in_pvs: false,
|
||||||
update_type,
|
update_type,
|
||||||
serial_number: 0,
|
serial_number: 0,
|
||||||
delay: None,
|
delay: None,
|
||||||
|
delta: None,
|
||||||
|
baseline_index: 0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -262,13 +294,8 @@ impl Parse<'_> for PacketEntitiesMessage {
|
||||||
|
|
||||||
let update_type = data.read()?;
|
let update_type = data.read()?;
|
||||||
if update_type == UpdateType::Enter {
|
if update_type == UpdateType::Enter {
|
||||||
let mut entity = Self::read_enter(
|
let mut entity =
|
||||||
&mut data,
|
Self::read_enter(&mut data, entity_index, state, base_line as usize, delta)?;
|
||||||
entity_index,
|
|
||||||
state,
|
|
||||||
base_line as usize,
|
|
||||||
delta.is_some(),
|
|
||||||
)?;
|
|
||||||
let send_table = get_send_table(state, entity.server_class)?;
|
let send_table = get_send_table(state, entity.server_class)?;
|
||||||
Self::read_update(&mut data, send_table, &mut entity.props, entity_index)?;
|
Self::read_update(&mut data, send_table, &mut entity.props, entity_index)?;
|
||||||
|
|
||||||
|
|
@ -287,12 +314,13 @@ impl Parse<'_> for PacketEntitiesMessage {
|
||||||
entities.push(PacketEntity {
|
entities.push(PacketEntity {
|
||||||
server_class: 0.into(),
|
server_class: 0.into(),
|
||||||
entity_index,
|
entity_index,
|
||||||
baseline_props: vec![],
|
|
||||||
props: vec![],
|
props: vec![],
|
||||||
in_pvs: false,
|
in_pvs: false,
|
||||||
update_type,
|
update_type,
|
||||||
serial_number: 0,
|
serial_number: 0,
|
||||||
delay: None,
|
delay: None,
|
||||||
|
delta,
|
||||||
|
baseline_index: 0,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -372,34 +400,23 @@ impl PacketEntitiesMessage {
|
||||||
entity_index: EntityId,
|
entity_index: EntityId,
|
||||||
state: &ParserState,
|
state: &ParserState,
|
||||||
baseline_index: usize,
|
baseline_index: usize,
|
||||||
is_delta: bool,
|
delta: Option<u32>,
|
||||||
) -> Result<PacketEntity> {
|
) -> Result<PacketEntity> {
|
||||||
let bits = log_base2(state.server_classes.len()) + 1;
|
let bits = log_base2(state.server_classes.len()) + 1;
|
||||||
let class_index: ClassId = stream.read_sized::<u16>(bits as usize)?.into();
|
let class_index: ClassId = stream.read_sized::<u16>(bits as usize)?.into();
|
||||||
|
|
||||||
let serial = stream.read_sized(10)?;
|
let serial = stream.read_sized(10)?;
|
||||||
let send_table = state
|
|
||||||
.send_tables
|
|
||||||
.get(usize::from(class_index))
|
|
||||||
.ok_or(ParseError::UnknownServerClass(class_index))?;
|
|
||||||
|
|
||||||
let baseline_props = state.get_baseline(
|
|
||||||
baseline_index,
|
|
||||||
entity_index,
|
|
||||||
class_index,
|
|
||||||
send_table,
|
|
||||||
is_delta,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(PacketEntity {
|
Ok(PacketEntity {
|
||||||
server_class: class_index,
|
server_class: class_index,
|
||||||
entity_index,
|
entity_index,
|
||||||
baseline_props,
|
|
||||||
props: vec![],
|
props: vec![],
|
||||||
in_pvs: true,
|
in_pvs: true,
|
||||||
update_type: UpdateType::Enter,
|
update_type: UpdateType::Enter,
|
||||||
serial_number: serial,
|
serial_number: serial,
|
||||||
delay: None,
|
delay: None,
|
||||||
|
delta,
|
||||||
|
baseline_index,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -583,12 +600,13 @@ fn test_packet_entitier_message_roundtrip() {
|
||||||
entities: vec![PacketEntity {
|
entities: vec![PacketEntity {
|
||||||
server_class: ClassId::from(0),
|
server_class: ClassId::from(0),
|
||||||
entity_index: Default::default(),
|
entity_index: Default::default(),
|
||||||
baseline_props: vec![],
|
|
||||||
props: vec![],
|
props: vec![],
|
||||||
in_pvs: true,
|
in_pvs: true,
|
||||||
update_type: UpdateType::Enter,
|
update_type: UpdateType::Enter,
|
||||||
serial_number: 0,
|
serial_number: 0,
|
||||||
delay: None,
|
delay: None,
|
||||||
|
delta: None,
|
||||||
|
baseline_index: 0,
|
||||||
}],
|
}],
|
||||||
removed_entities: vec![],
|
removed_entities: vec![],
|
||||||
max_entries: 4,
|
max_entries: 4,
|
||||||
|
|
@ -604,17 +622,17 @@ fn test_packet_entitier_message_roundtrip() {
|
||||||
PacketEntity {
|
PacketEntity {
|
||||||
server_class: ClassId::from(0),
|
server_class: ClassId::from(0),
|
||||||
entity_index: EntityId::from(0u32),
|
entity_index: EntityId::from(0u32),
|
||||||
baseline_props: vec![],
|
|
||||||
props: vec![],
|
props: vec![],
|
||||||
in_pvs: true,
|
in_pvs: true,
|
||||||
update_type: UpdateType::Enter,
|
update_type: UpdateType::Enter,
|
||||||
serial_number: 0,
|
serial_number: 0,
|
||||||
delay: None,
|
delay: None,
|
||||||
|
delta: None,
|
||||||
|
baseline_index: 0,
|
||||||
},
|
},
|
||||||
PacketEntity {
|
PacketEntity {
|
||||||
server_class: ClassId::from(1),
|
server_class: ClassId::from(1),
|
||||||
entity_index: EntityId::from(4u32),
|
entity_index: EntityId::from(4u32),
|
||||||
baseline_props: vec![],
|
|
||||||
props: vec![
|
props: vec![
|
||||||
SendProp {
|
SendProp {
|
||||||
index: 0,
|
index: 0,
|
||||||
|
|
@ -631,11 +649,14 @@ fn test_packet_entitier_message_roundtrip() {
|
||||||
update_type: UpdateType::Preserve,
|
update_type: UpdateType::Preserve,
|
||||||
serial_number: 0,
|
serial_number: 0,
|
||||||
delay: None,
|
delay: None,
|
||||||
|
delta: None,
|
||||||
|
baseline_index: 0,
|
||||||
},
|
},
|
||||||
PacketEntity {
|
PacketEntity {
|
||||||
server_class: ClassId::from(1),
|
server_class: ClassId::from(1),
|
||||||
entity_index: EntityId::from(5u32),
|
entity_index: EntityId::from(5u32),
|
||||||
baseline_props: vec![],
|
delta: None,
|
||||||
|
baseline_index: 0,
|
||||||
props: vec![
|
props: vec![
|
||||||
SendProp {
|
SendProp {
|
||||||
index: 0,
|
index: 0,
|
||||||
|
|
|
||||||
|
|
@ -160,10 +160,10 @@ impl MessageHandler for GameStateAnalyser {
|
||||||
matches!(message_type, MessageType::PacketEntities)
|
matches!(message_type, MessageType::PacketEntities)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_message(&mut self, message: &Message, _tick: u32, _parser_state: &ParserState) {
|
fn handle_message(&mut self, message: &Message, _tick: u32, parser_state: &ParserState) {
|
||||||
if let Message::PacketEntities(message) = message {
|
if let Message::PacketEntities(message) = message {
|
||||||
for entity in &message.entities {
|
for entity in &message.entities {
|
||||||
self.handle_entity(entity);
|
self.handle_entity(entity, parser_state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -221,22 +221,22 @@ impl GameStateAnalyser {
|
||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_entity(&mut self, entity: &PacketEntity) {
|
pub fn handle_entity(&mut self, entity: &PacketEntity, parser_state: &ParserState) {
|
||||||
let class_name: &str = self
|
let class_name: &str = self
|
||||||
.class_names
|
.class_names
|
||||||
.get(usize::from(entity.server_class))
|
.get(usize::from(entity.server_class))
|
||||||
.map(|class_name| class_name.as_str())
|
.map(|class_name| class_name.as_str())
|
||||||
.unwrap_or("");
|
.unwrap_or("");
|
||||||
match class_name {
|
match class_name {
|
||||||
"CTFPlayer" => self.handle_player_entity(entity),
|
"CTFPlayer" => self.handle_player_entity(entity, parser_state),
|
||||||
"CTFPlayerResource" => self.handle_player_resource(entity),
|
"CTFPlayerResource" => self.handle_player_resource(entity, parser_state),
|
||||||
"CWorld" => self.handle_world_entity(entity),
|
"CWorld" => self.handle_world_entity(entity, parser_state),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_player_resource(&mut self, entity: &PacketEntity) {
|
pub fn handle_player_resource(&mut self, entity: &PacketEntity, parser_state: &ParserState) {
|
||||||
for prop in entity.props() {
|
for prop in entity.props(parser_state) {
|
||||||
if let Some((table_name, prop_name)) = self.prop_names.get(&prop.identifier) {
|
if let Some((table_name, prop_name)) = self.prop_names.get(&prop.identifier) {
|
||||||
if let Ok(player_id) = u32::from_str(prop_name.as_str()) {
|
if let Ok(player_id) = u32::from_str(prop_name.as_str()) {
|
||||||
let entity_id = EntityId::from(player_id);
|
let entity_id = EntityId::from(player_id);
|
||||||
|
|
@ -267,7 +267,7 @@ impl GameStateAnalyser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_player_entity(&mut self, entity: &PacketEntity) {
|
pub fn handle_player_entity(&mut self, entity: &PacketEntity, parser_state: &ParserState) {
|
||||||
let player = self.state.get_or_create_player(entity.entity_index);
|
let player = self.state.get_or_create_player(entity.entity_index);
|
||||||
|
|
||||||
const HEALTH_PROP: SendPropIdentifier =
|
const HEALTH_PROP: SendPropIdentifier =
|
||||||
|
|
@ -294,7 +294,7 @@ impl GameStateAnalyser {
|
||||||
const NON_LOCAL_PITCH_ANGLES: SendPropIdentifier =
|
const NON_LOCAL_PITCH_ANGLES: SendPropIdentifier =
|
||||||
SendPropIdentifier::new("DT_TFNonLocalPlayerExclusive", "m_angEyeAngles[0]");
|
SendPropIdentifier::new("DT_TFNonLocalPlayerExclusive", "m_angEyeAngles[0]");
|
||||||
|
|
||||||
for prop in entity.props() {
|
for prop in entity.props(parser_state) {
|
||||||
match prop.identifier {
|
match prop.identifier {
|
||||||
HEALTH_PROP => {
|
HEALTH_PROP => {
|
||||||
player.health = i64::try_from(&prop.value).unwrap_or_default() as u16
|
player.health = i64::try_from(&prop.value).unwrap_or_default() as u16
|
||||||
|
|
@ -324,7 +324,7 @@ impl GameStateAnalyser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_world_entity(&mut self, entity: &PacketEntity) {
|
pub fn handle_world_entity(&mut self, entity: &PacketEntity, parser_state: &ParserState) {
|
||||||
if let (
|
if let (
|
||||||
Some(SendProp {
|
Some(SendProp {
|
||||||
value: SendPropValue::Vector(boundary_min),
|
value: SendPropValue::Vector(boundary_min),
|
||||||
|
|
@ -335,12 +335,12 @@ impl GameStateAnalyser {
|
||||||
..
|
..
|
||||||
}),
|
}),
|
||||||
) = (
|
) = (
|
||||||
entity.get_prop_by_name("DT_WORLD", "m_WorldMins"),
|
entity.get_prop_by_name("DT_WORLD", "m_WorldMins", parser_state),
|
||||||
entity.get_prop_by_name("DT_WORLD", "m_WorldMaxs"),
|
entity.get_prop_by_name("DT_WORLD", "m_WorldMaxs", parser_state),
|
||||||
) {
|
) {
|
||||||
self.state.world = Some(World {
|
self.state.world = Some(World {
|
||||||
boundary_min: *boundary_min,
|
boundary_min,
|
||||||
boundary_max: *boundary_max,
|
boundary_max,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use crate::demo::message::{Message, MessageType};
|
||||||
use crate::demo::packet::datatable::{ParseSendTable, ServerClass};
|
use crate::demo::packet::datatable::{ParseSendTable, ServerClass};
|
||||||
use crate::demo::packet::stringtable::{StringTable, StringTableEntry};
|
use crate::demo::packet::stringtable::{StringTable, StringTableEntry};
|
||||||
use crate::demo::packet::Packet;
|
use crate::demo::packet::Packet;
|
||||||
use crate::{ParseError, Result};
|
use crate::Result;
|
||||||
|
|
||||||
use crate::demo::header::Header;
|
use crate::demo::header::Header;
|
||||||
use crate::demo::packet::message::MessagePacketMeta;
|
use crate::demo::packet::message::MessagePacketMeta;
|
||||||
|
|
@ -110,7 +110,6 @@ 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<()> {
|
||||||
let mut baselines_updated = false;
|
|
||||||
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)?;
|
||||||
|
|
@ -123,7 +122,6 @@ impl<'a, T: MessageHandler> DemoHandler<'a, T> {
|
||||||
Packet::Message(packet) | Packet::Signon(packet) => {
|
Packet::Message(packet) | Packet::Signon(packet) => {
|
||||||
self.analyser
|
self.analyser
|
||||||
.handle_packet_meta(packet.tick, &packet.meta, &self.state_handler);
|
.handle_packet_meta(packet.tick, &packet.meta, &self.state_handler);
|
||||||
//self.tick = packet.tick;
|
|
||||||
for message in packet.messages {
|
for message in packet.messages {
|
||||||
match message {
|
match message {
|
||||||
Message::NetTick(message) => self.tick = message.tick,
|
Message::NetTick(message) => self.tick = message.tick,
|
||||||
|
|
@ -131,28 +129,9 @@ impl<'a, T: MessageHandler> DemoHandler<'a, T> {
|
||||||
self.handle_string_table(message.table)
|
self.handle_string_table(message.table)
|
||||||
}
|
}
|
||||||
Message::UpdateStringTable(message) => {
|
Message::UpdateStringTable(message) => {
|
||||||
baselines_updated = true;
|
|
||||||
self.handle_table_update(message.table_id, message.entries)
|
self.handle_table_update(message.table_id, message.entries)
|
||||||
}
|
}
|
||||||
Message::PacketEntities(mut msg) => {
|
Message::PacketEntities(msg) => {
|
||||||
if baselines_updated {
|
|
||||||
// if baselines were updated in the same packet, the newly added
|
|
||||||
// static baselines wont be used yet, patch it up afterward
|
|
||||||
for ent in msg.entities.iter_mut() {
|
|
||||||
if ent.baseline_props.is_empty() {
|
|
||||||
let send_table = self
|
|
||||||
.state_handler
|
|
||||||
.send_tables
|
|
||||||
.get(usize::from(ent.server_class))
|
|
||||||
.ok_or(ParseError::UnknownServerClass(
|
|
||||||
ent.server_class,
|
|
||||||
))?;
|
|
||||||
ent.baseline_props = self
|
|
||||||
.state_handler
|
|
||||||
.get_static_baseline(ent.server_class, send_table)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.handle_message(Message::PacketEntities(msg))
|
self.handle_message(Message::PacketEntities(msg))
|
||||||
}
|
}
|
||||||
message => self.handle_message(message),
|
message => self.handle_message(message),
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use fnv::FnvHashMap;
|
use fnv::FnvHashMap;
|
||||||
|
use std::borrow::Cow;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::demo::gamevent::GameEventDefinition;
|
use crate::demo::gamevent::GameEventDefinition;
|
||||||
|
|
@ -97,24 +98,35 @@ impl<'a> ParserState {
|
||||||
class_id: ClassId,
|
class_id: ClassId,
|
||||||
send_table: &SendTable,
|
send_table: &SendTable,
|
||||||
) -> Result<Vec<SendProp>> {
|
) -> Result<Vec<SendProp>> {
|
||||||
let mut cached = self.parsed_static_baselines.borrow_mut();
|
match self.static_baselines.get(&class_id) {
|
||||||
Ok(match cached.get(&class_id) {
|
Some(static_baseline) => static_baseline.parse(send_table),
|
||||||
Some(props) => props.clone(),
|
None => {
|
||||||
None => match self.static_baselines.get(&class_id) {
|
#[cfg(feature = "trace")]
|
||||||
Some(static_baseline) => {
|
warn!(
|
||||||
let props = static_baseline.parse(send_table)?;
|
class_id = display(class_id),
|
||||||
cached.entry(class_id).or_insert(props).clone()
|
"class without static baseline"
|
||||||
}
|
);
|
||||||
None => {
|
Ok(Vec::new())
|
||||||
#[cfg(feature = "trace")]
|
}
|
||||||
warn!(
|
}
|
||||||
class_id = display(class_id),
|
// let mut cached = self.parsed_static_baselines.borrow_mut();
|
||||||
"class without static baseline"
|
// Ok(match cached.entry(class_id) {
|
||||||
);
|
// Entry::Occupied(entry) => entry.get().as_slice(),
|
||||||
Vec::with_capacity(8)
|
// Entry::Vacant(entry) => match self.static_baselines.get(&class_id) {
|
||||||
}
|
// Some(static_baseline) => {
|
||||||
},
|
// let props = static_baseline.parse(send_table)?;
|
||||||
})
|
// entry.insert(props).as_slice()
|
||||||
|
// }
|
||||||
|
// None => {
|
||||||
|
// #[cfg(feature = "trace")]
|
||||||
|
// warn!(
|
||||||
|
// class_id = display(class_id),
|
||||||
|
// "class without static baseline"
|
||||||
|
// );
|
||||||
|
// &[]
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_baseline(
|
pub fn get_baseline(
|
||||||
|
|
@ -124,20 +136,22 @@ impl<'a> ParserState {
|
||||||
class_id: ClassId,
|
class_id: ClassId,
|
||||||
send_table: &SendTable,
|
send_table: &SendTable,
|
||||||
is_delta: bool,
|
is_delta: bool,
|
||||||
) -> Result<Vec<SendProp>> {
|
) -> Result<Cow<[SendProp]>> {
|
||||||
match self.instance_baselines[baseline_index].get(entity_index) {
|
match self.instance_baselines[baseline_index].get(entity_index) {
|
||||||
Some(baseline) if baseline.server_class == class_id && is_delta => {
|
Some(baseline) if baseline.server_class == class_id && is_delta => {
|
||||||
Ok(baseline.props.clone())
|
Ok(Cow::Borrowed(&baseline.props))
|
||||||
}
|
}
|
||||||
_ => match self.static_baselines.get(&class_id) {
|
_ => match self.static_baselines.get(&class_id) {
|
||||||
Some(_static_baseline) => self.get_static_baseline(class_id, send_table),
|
Some(_static_baseline) => {
|
||||||
|
Ok(Cow::Owned(self.get_static_baseline(class_id, send_table)?))
|
||||||
|
}
|
||||||
None => {
|
None => {
|
||||||
#[cfg(feature = "trace")]
|
#[cfg(feature = "trace")]
|
||||||
warn!(
|
warn!(
|
||||||
class_id = display(class_id),
|
class_id = display(class_id),
|
||||||
"class without static baseline"
|
"class without static baseline"
|
||||||
);
|
);
|
||||||
Ok(Vec::with_capacity(8))
|
Ok(Cow::Owned(Vec::new()))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -381,12 +395,13 @@ impl From<BaselineEntity> for PacketEntity {
|
||||||
PacketEntity {
|
PacketEntity {
|
||||||
server_class: baseline.server_class,
|
server_class: baseline.server_class,
|
||||||
entity_index: baseline.entity_id,
|
entity_index: baseline.entity_id,
|
||||||
baseline_props: vec![],
|
|
||||||
props: baseline.props,
|
props: baseline.props,
|
||||||
in_pvs: false,
|
in_pvs: false,
|
||||||
update_type: UpdateType::Enter,
|
update_type: UpdateType::Enter,
|
||||||
serial_number: baseline.serial,
|
serial_number: baseline.serial,
|
||||||
delay: None,
|
delay: None,
|
||||||
|
delta: None,
|
||||||
|
baseline_index: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@ impl EntityDump {
|
||||||
tick: u32,
|
tick: u32,
|
||||||
classes: &[ServerClass],
|
classes: &[ServerClass],
|
||||||
prop_names: &FnvHashMap<SendPropIdentifier, (SendTableName, SendPropName)>,
|
prop_names: &FnvHashMap<SendPropIdentifier, (SendTableName, SendPropName)>,
|
||||||
|
state: &ParserState,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
EntityDump {
|
EntityDump {
|
||||||
tick,
|
tick,
|
||||||
|
|
@ -59,7 +60,7 @@ impl EntityDump {
|
||||||
id: entity.entity_index,
|
id: entity.entity_index,
|
||||||
pvs: entity.update_type.into(),
|
pvs: entity.update_type.into(),
|
||||||
props: entity
|
props: entity
|
||||||
.into_props()
|
.props(state)
|
||||||
.map(|prop| {
|
.map(|prop| {
|
||||||
let (table_name, prop_name) = &prop_names[&prop.identifier];
|
let (table_name, prop_name) = &prop_names[&prop.identifier];
|
||||||
(format!("{}.{}", table_name, prop_name), prop.value)
|
(format!("{}.{}", table_name, prop_name), prop.value)
|
||||||
|
|
@ -126,7 +127,7 @@ impl MessageHandler for EntityDumper {
|
||||||
self.entities
|
self.entities
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(tick, entity)| {
|
.map(|(tick, entity)| {
|
||||||
EntityDump::from_entity(entity, tick, &state.server_classes, &prop_names)
|
EntityDump::from_entity(entity, tick, &state.server_classes, &prop_names, state)
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue