mirror of
https://codeberg.org/demostf/parser.git
synced 2026-06-03 18:24:05 +02:00
rework baseline prop handling
This commit is contained in:
parent
74ad7d121f
commit
bae9acdd92
9 changed files with 135 additions and 105 deletions
|
|
@ -12,6 +12,12 @@ pub enum MaybeUtf8String {
|
|||
Invalid(Vec<u8>),
|
||||
}
|
||||
|
||||
impl From<&'_ str> for MaybeUtf8String {
|
||||
fn from(str: &'_ str) -> Self {
|
||||
MaybeUtf8String::Valid(str.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for MaybeUtf8String {
|
||||
fn default() -> Self {
|
||||
MaybeUtf8String::Valid(String::new())
|
||||
|
|
@ -62,9 +68,15 @@ impl<'a, E: Endianness> BitRead<'a, E> for MaybeUtf8String {
|
|||
match String::read(stream) {
|
||||
Ok(str) => Ok(MaybeUtf8String::Valid(str)),
|
||||
Err(bitbuffer::BitError::Utf8Error(_, size)) => {
|
||||
stream.set_pos(stream.pos() - size * 8)?;
|
||||
let data = stream.read_sized(size)?;
|
||||
Ok(MaybeUtf8String::Invalid(data))
|
||||
stream.set_pos(stream.pos().saturating_sub(size * 8))?;
|
||||
let mut data: Vec<u8> = stream.read_sized(size)?;
|
||||
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),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use bitbuffer::{BitRead, BitReadSized, BitWrite, BitWriteSized, BitWriteStream, LittleEndian};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||
use std::borrow::Cow;
|
||||
|
||||
use crate::demo::message::stringtable::log_base2;
|
||||
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 std::cmp::{min, Ordering};
|
||||
|
||||
use itertools::Either;
|
||||
use std::fmt;
|
||||
use std::num::NonZeroU32;
|
||||
#[cfg(feature = "trace")]
|
||||
|
|
@ -87,12 +89,13 @@ pub enum UpdateType {
|
|||
pub struct PacketEntity {
|
||||
pub server_class: ClassId,
|
||||
pub entity_index: EntityId,
|
||||
pub baseline_props: Vec<SendProp>,
|
||||
pub props: Vec<SendProp>,
|
||||
pub in_pvs: bool,
|
||||
pub update_type: UpdateType,
|
||||
pub serial_number: u32,
|
||||
pub delay: Option<f32>,
|
||||
pub delta: Option<u32>,
|
||||
pub baseline_index: usize,
|
||||
}
|
||||
|
||||
impl fmt::Display for PacketEntity {
|
||||
|
|
@ -110,8 +113,13 @@ impl PacketEntity {
|
|||
self.props.iter_mut().find(|prop| prop.identifier == *index)
|
||||
}
|
||||
|
||||
pub fn get_prop_by_identifier(&self, index: &SendPropIdentifier) -> Option<&SendProp> {
|
||||
self.props().find(|prop| prop.identifier == *index)
|
||||
pub fn get_prop_by_identifier(
|
||||
&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]) {
|
||||
|
|
@ -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);
|
||||
self.get_prop_by_identifier(&identifier)
|
||||
self.get_prop_by_identifier(&identifier, parser_state)
|
||||
}
|
||||
|
||||
pub fn props(&self) -> impl Iterator<Item = &SendProp> {
|
||||
self.baseline_props.iter().chain(self.props.iter())
|
||||
fn get_baseline_props<'a>(&self, parser_state: &'a ParserState) -> Cow<'a, [SendProp]> {
|
||||
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> {
|
||||
self.baseline_props
|
||||
.into_iter()
|
||||
.chain(self.props.into_iter())
|
||||
pub fn props<'a>(
|
||||
&'a self,
|
||||
parser_state: &'a ParserState,
|
||||
) -> 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 {
|
||||
server_class: class_id,
|
||||
entity_index,
|
||||
baseline_props: vec![],
|
||||
props: Vec::with_capacity(8),
|
||||
in_pvs: false,
|
||||
update_type,
|
||||
serial_number: 0,
|
||||
delay: None,
|
||||
delta: None,
|
||||
baseline_index: 0,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -262,13 +294,8 @@ impl Parse<'_> for PacketEntitiesMessage {
|
|||
|
||||
let update_type = data.read()?;
|
||||
if update_type == UpdateType::Enter {
|
||||
let mut entity = Self::read_enter(
|
||||
&mut data,
|
||||
entity_index,
|
||||
state,
|
||||
base_line as usize,
|
||||
delta.is_some(),
|
||||
)?;
|
||||
let mut entity =
|
||||
Self::read_enter(&mut data, entity_index, state, base_line as usize, delta)?;
|
||||
let send_table = get_send_table(state, entity.server_class)?;
|
||||
Self::read_update(&mut data, send_table, &mut entity.props, entity_index)?;
|
||||
|
||||
|
|
@ -287,12 +314,13 @@ impl Parse<'_> for PacketEntitiesMessage {
|
|||
entities.push(PacketEntity {
|
||||
server_class: 0.into(),
|
||||
entity_index,
|
||||
baseline_props: vec![],
|
||||
props: vec![],
|
||||
in_pvs: false,
|
||||
update_type,
|
||||
serial_number: 0,
|
||||
delay: None,
|
||||
delta,
|
||||
baseline_index: 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -372,34 +400,23 @@ impl PacketEntitiesMessage {
|
|||
entity_index: EntityId,
|
||||
state: &ParserState,
|
||||
baseline_index: usize,
|
||||
is_delta: bool,
|
||||
delta: Option<u32>,
|
||||
) -> Result<PacketEntity> {
|
||||
let bits = log_base2(state.server_classes.len()) + 1;
|
||||
let class_index: ClassId = stream.read_sized::<u16>(bits as usize)?.into();
|
||||
|
||||
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 {
|
||||
server_class: class_index,
|
||||
entity_index,
|
||||
baseline_props,
|
||||
props: vec![],
|
||||
in_pvs: true,
|
||||
update_type: UpdateType::Enter,
|
||||
serial_number: serial,
|
||||
delay: None,
|
||||
delta,
|
||||
baseline_index,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -583,12 +600,13 @@ fn test_packet_entitier_message_roundtrip() {
|
|||
entities: vec![PacketEntity {
|
||||
server_class: ClassId::from(0),
|
||||
entity_index: Default::default(),
|
||||
baseline_props: vec![],
|
||||
props: vec![],
|
||||
in_pvs: true,
|
||||
update_type: UpdateType::Enter,
|
||||
serial_number: 0,
|
||||
delay: None,
|
||||
delta: None,
|
||||
baseline_index: 0,
|
||||
}],
|
||||
removed_entities: vec![],
|
||||
max_entries: 4,
|
||||
|
|
@ -604,17 +622,17 @@ fn test_packet_entitier_message_roundtrip() {
|
|||
PacketEntity {
|
||||
server_class: ClassId::from(0),
|
||||
entity_index: EntityId::from(0u32),
|
||||
baseline_props: vec![],
|
||||
props: vec![],
|
||||
in_pvs: true,
|
||||
update_type: UpdateType::Enter,
|
||||
serial_number: 0,
|
||||
delay: None,
|
||||
delta: None,
|
||||
baseline_index: 0,
|
||||
},
|
||||
PacketEntity {
|
||||
server_class: ClassId::from(1),
|
||||
entity_index: EntityId::from(4u32),
|
||||
baseline_props: vec![],
|
||||
props: vec![
|
||||
SendProp {
|
||||
index: 0,
|
||||
|
|
@ -631,11 +649,14 @@ fn test_packet_entitier_message_roundtrip() {
|
|||
update_type: UpdateType::Preserve,
|
||||
serial_number: 0,
|
||||
delay: None,
|
||||
delta: None,
|
||||
baseline_index: 0,
|
||||
},
|
||||
PacketEntity {
|
||||
server_class: ClassId::from(1),
|
||||
entity_index: EntityId::from(5u32),
|
||||
baseline_props: vec![],
|
||||
delta: None,
|
||||
baseline_index: 0,
|
||||
props: vec![
|
||||
SendProp {
|
||||
index: 0,
|
||||
|
|
|
|||
|
|
@ -160,10 +160,10 @@ impl MessageHandler for GameStateAnalyser {
|
|||
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 {
|
||||
for entity in &message.entities {
|
||||
self.handle_entity(entity);
|
||||
self.handle_entity(entity, parser_state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -221,22 +221,22 @@ impl GameStateAnalyser {
|
|||
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
|
||||
.class_names
|
||||
.get(usize::from(entity.server_class))
|
||||
.map(|class_name| class_name.as_str())
|
||||
.unwrap_or("");
|
||||
match class_name {
|
||||
"CTFPlayer" => self.handle_player_entity(entity),
|
||||
"CTFPlayerResource" => self.handle_player_resource(entity),
|
||||
"CWorld" => self.handle_world_entity(entity),
|
||||
"CTFPlayer" => self.handle_player_entity(entity, parser_state),
|
||||
"CTFPlayerResource" => self.handle_player_resource(entity, parser_state),
|
||||
"CWorld" => self.handle_world_entity(entity, parser_state),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_player_resource(&mut self, entity: &PacketEntity) {
|
||||
for prop in entity.props() {
|
||||
pub fn handle_player_resource(&mut self, entity: &PacketEntity, parser_state: &ParserState) {
|
||||
for prop in entity.props(parser_state) {
|
||||
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()) {
|
||||
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);
|
||||
|
||||
const HEALTH_PROP: SendPropIdentifier =
|
||||
|
|
@ -294,7 +294,7 @@ impl GameStateAnalyser {
|
|||
const NON_LOCAL_PITCH_ANGLES: SendPropIdentifier =
|
||||
SendPropIdentifier::new("DT_TFNonLocalPlayerExclusive", "m_angEyeAngles[0]");
|
||||
|
||||
for prop in entity.props() {
|
||||
for prop in entity.props(parser_state) {
|
||||
match prop.identifier {
|
||||
HEALTH_PROP => {
|
||||
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 (
|
||||
Some(SendProp {
|
||||
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_WorldMaxs"),
|
||||
entity.get_prop_by_name("DT_WORLD", "m_WorldMins", parser_state),
|
||||
entity.get_prop_by_name("DT_WORLD", "m_WorldMaxs", parser_state),
|
||||
) {
|
||||
self.state.world = Some(World {
|
||||
boundary_min: *boundary_min,
|
||||
boundary_max: *boundary_max,
|
||||
boundary_min,
|
||||
boundary_max,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use crate::demo::message::{Message, MessageType};
|
|||
use crate::demo::packet::datatable::{ParseSendTable, ServerClass};
|
||||
use crate::demo::packet::stringtable::{StringTable, StringTableEntry};
|
||||
use crate::demo::packet::Packet;
|
||||
use crate::{ParseError, Result};
|
||||
use crate::Result;
|
||||
|
||||
use crate::demo::header::Header;
|
||||
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<()> {
|
||||
let mut baselines_updated = false;
|
||||
match packet {
|
||||
Packet::DataTables(packet) => {
|
||||
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) => {
|
||||
self.analyser
|
||||
.handle_packet_meta(packet.tick, &packet.meta, &self.state_handler);
|
||||
//self.tick = packet.tick;
|
||||
for message in packet.messages {
|
||||
match message {
|
||||
Message::NetTick(message) => self.tick = message.tick,
|
||||
|
|
@ -131,28 +129,9 @@ impl<'a, T: MessageHandler> DemoHandler<'a, T> {
|
|||
self.handle_string_table(message.table)
|
||||
}
|
||||
Message::UpdateStringTable(message) => {
|
||||
baselines_updated = true;
|
||||
self.handle_table_update(message.table_id, message.entries)
|
||||
}
|
||||
Message::PacketEntities(mut 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)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Message::PacketEntities(msg) => {
|
||||
self.handle_message(Message::PacketEntities(msg))
|
||||
}
|
||||
message => self.handle_message(message),
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use fnv::FnvHashMap;
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::demo::gamevent::GameEventDefinition;
|
||||
|
|
@ -97,24 +98,35 @@ impl<'a> ParserState {
|
|||
class_id: ClassId,
|
||||
send_table: &SendTable,
|
||||
) -> Result<Vec<SendProp>> {
|
||||
let mut cached = self.parsed_static_baselines.borrow_mut();
|
||||
Ok(match cached.get(&class_id) {
|
||||
Some(props) => props.clone(),
|
||||
None => match self.static_baselines.get(&class_id) {
|
||||
Some(static_baseline) => {
|
||||
let props = static_baseline.parse(send_table)?;
|
||||
cached.entry(class_id).or_insert(props).clone()
|
||||
}
|
||||
None => {
|
||||
#[cfg(feature = "trace")]
|
||||
warn!(
|
||||
class_id = display(class_id),
|
||||
"class without static baseline"
|
||||
);
|
||||
Vec::with_capacity(8)
|
||||
}
|
||||
},
|
||||
})
|
||||
match self.static_baselines.get(&class_id) {
|
||||
Some(static_baseline) => static_baseline.parse(send_table),
|
||||
None => {
|
||||
#[cfg(feature = "trace")]
|
||||
warn!(
|
||||
class_id = display(class_id),
|
||||
"class without static baseline"
|
||||
);
|
||||
Ok(Vec::new())
|
||||
}
|
||||
}
|
||||
// let mut cached = self.parsed_static_baselines.borrow_mut();
|
||||
// Ok(match cached.entry(class_id) {
|
||||
// Entry::Occupied(entry) => entry.get().as_slice(),
|
||||
// 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(
|
||||
|
|
@ -124,20 +136,22 @@ impl<'a> ParserState {
|
|||
class_id: ClassId,
|
||||
send_table: &SendTable,
|
||||
is_delta: bool,
|
||||
) -> Result<Vec<SendProp>> {
|
||||
) -> Result<Cow<[SendProp]>> {
|
||||
match self.instance_baselines[baseline_index].get(entity_index) {
|
||||
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) {
|
||||
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 => {
|
||||
#[cfg(feature = "trace")]
|
||||
warn!(
|
||||
class_id = display(class_id),
|
||||
"class without static baseline"
|
||||
);
|
||||
Ok(Vec::with_capacity(8))
|
||||
Ok(Cow::Owned(Vec::new()))
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
@ -381,12 +395,13 @@ impl From<BaselineEntity> for PacketEntity {
|
|||
PacketEntity {
|
||||
server_class: baseline.server_class,
|
||||
entity_index: baseline.entity_id,
|
||||
baseline_props: vec![],
|
||||
props: baseline.props,
|
||||
in_pvs: false,
|
||||
update_type: UpdateType::Enter,
|
||||
serial_number: baseline.serial,
|
||||
delay: None,
|
||||
delta: None,
|
||||
baseline_index: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue