mirror of
https://codeberg.org/demostf/parser.git
synced 2026-06-03 10:14:06 +02:00
packetentities write?
This commit is contained in:
parent
bfd6d8caf9
commit
4843fe0600
5 changed files with 327 additions and 15 deletions
|
|
@ -1,11 +1,14 @@
|
||||||
use bitbuffer::{BitRead, BitReadSized, 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 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::{Encode, ParseBitSkip};
|
||||||
use crate::demo::sendprop::{SendProp, SendPropIdentifier, SendPropValue};
|
use crate::demo::sendprop::{
|
||||||
|
FloatDefinition, SendProp, SendPropDefinition, SendPropIdentifier, SendPropParseDefinition,
|
||||||
|
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;
|
||||||
|
|
@ -37,7 +40,9 @@ impl From<u32> for EntityId {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(BitRead, Clone, Copy, Debug, PartialEq, Eq, Serialize_repr, Deserialize_repr)]
|
#[derive(
|
||||||
|
BitRead, BitWrite, Clone, Copy, Debug, PartialEq, Eq, Serialize_repr, Deserialize_repr,
|
||||||
|
)]
|
||||||
#[discriminant_bits = 2]
|
#[discriminant_bits = 2]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum PVS {
|
pub enum PVS {
|
||||||
|
|
@ -47,7 +52,7 @@ pub enum PVS {
|
||||||
Delete = 3,
|
Delete = 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct PacketEntity {
|
pub struct PacketEntity {
|
||||||
pub server_class: ClassId,
|
pub server_class: ClassId,
|
||||||
pub entity_index: EntityId,
|
pub entity_index: EntityId,
|
||||||
|
|
@ -90,6 +95,21 @@ impl PacketEntity {
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn diff_from_baseline<'a>(
|
||||||
|
&self,
|
||||||
|
baseline: &'a [SendProp],
|
||||||
|
) -> impl Iterator<Item = &'a SendProp> + 'a {
|
||||||
|
baseline.iter().filter(move |prop| {
|
||||||
|
match baseline
|
||||||
|
.iter()
|
||||||
|
.find(|base_prop| base_prop.index == prop.index)
|
||||||
|
{
|
||||||
|
Some(base_prop) => base_prop.value != prop.value,
|
||||||
|
None => true,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_bit_var<'a, T: BitReadSized<'a, LittleEndian>>(stream: &mut Stream<'a>) -> ReadResult<T> {
|
fn read_bit_var<'a, T: BitReadSized<'a, LittleEndian>>(stream: &mut Stream<'a>) -> ReadResult<T> {
|
||||||
|
|
@ -105,7 +125,56 @@ fn read_bit_var<'a, T: BitReadSized<'a, LittleEndian>>(stream: &mut Stream<'a>)
|
||||||
stream.read_sized(bits)
|
stream.read_sized(bits)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
fn write_bit_var(var: u32, stream: &mut BitWriteStream<LittleEndian>) -> ReadResult<()> {
|
||||||
|
let ty: u8 = if var >= 2u32.pow(12) {
|
||||||
|
3
|
||||||
|
} else if var >= 2u32.pow(8) {
|
||||||
|
2
|
||||||
|
} else if var >= 2u32.pow(4) {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
ty.write_sized(stream, 2)?;
|
||||||
|
|
||||||
|
let bits = match ty {
|
||||||
|
0 => 4,
|
||||||
|
1 => 8,
|
||||||
|
2 => 12,
|
||||||
|
3 => 32,
|
||||||
|
_ => unsafe { unreachable_unchecked() },
|
||||||
|
};
|
||||||
|
|
||||||
|
var.write_sized(stream, bits)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bit_var_roundtrip() {
|
||||||
|
use bitbuffer::{BitReadBuffer, BitReadStream};
|
||||||
|
|
||||||
|
fn bit_var_normal(val: u32) {
|
||||||
|
let mut data = Vec::with_capacity(16);
|
||||||
|
let pos = {
|
||||||
|
let mut write = BitWriteStream::new(&mut data, LittleEndian);
|
||||||
|
write_bit_var(val, &mut write).unwrap();
|
||||||
|
write.bit_len()
|
||||||
|
};
|
||||||
|
let mut read = BitReadStream::new(BitReadBuffer::new(&data, LittleEndian));
|
||||||
|
assert_eq!(val, read_bit_var(&mut read).unwrap());
|
||||||
|
assert_eq!(pos, read.pos());
|
||||||
|
}
|
||||||
|
bit_var_normal(0);
|
||||||
|
bit_var_normal(1);
|
||||||
|
bit_var_normal(24);
|
||||||
|
bit_var_normal(1234);
|
||||||
|
bit_var_normal(12345);
|
||||||
|
bit_var_normal(123456);
|
||||||
|
bit_var_normal(1234567);
|
||||||
|
bit_var_normal(12345678);
|
||||||
|
bit_var_normal(123456789);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct PacketEntitiesMessage {
|
pub struct PacketEntitiesMessage {
|
||||||
pub entities: Vec<PacketEntity>,
|
pub entities: Vec<PacketEntity>,
|
||||||
pub removed_entities: Vec<EntityId>,
|
pub removed_entities: Vec<EntityId>,
|
||||||
|
|
@ -201,6 +270,69 @@ impl Parse<'_> for PacketEntitiesMessage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Encode for PacketEntitiesMessage {
|
||||||
|
fn encode(&self, stream: &mut BitWriteStream<LittleEndian>, state: &ParserState) -> Result<()> {
|
||||||
|
self.max_entries.write_sized(stream, 11)?;
|
||||||
|
self.delta.is_some().write(stream)?;
|
||||||
|
if let Some(delta) = self.delta {
|
||||||
|
delta.get().write(stream)?;
|
||||||
|
}
|
||||||
|
self.base_line.write_sized(stream, 1)?;
|
||||||
|
self.entities.len().write_sized(stream, 11)?;
|
||||||
|
|
||||||
|
stream.reserve(20, |length_stream, stream| {
|
||||||
|
self.updated_base_line.write(stream)?;
|
||||||
|
|
||||||
|
let length_start = stream.bit_len();
|
||||||
|
|
||||||
|
let mut last_index: i32 = -1;
|
||||||
|
|
||||||
|
for entity in self.entities.iter() {
|
||||||
|
let diff = entity.entity_index.0 as i32 - last_index - 1;
|
||||||
|
write_bit_var(diff as u32, stream)?;
|
||||||
|
last_index = entity.entity_index.0 as i32;
|
||||||
|
|
||||||
|
entity.pvs.write(stream)?;
|
||||||
|
|
||||||
|
let send_table = get_send_table(state, entity.server_class)?;
|
||||||
|
match entity.pvs {
|
||||||
|
PVS::Enter => {
|
||||||
|
Self::write_enter(entity, stream, state)?;
|
||||||
|
let baseline = state.get_baseline(
|
||||||
|
self.base_line as usize,
|
||||||
|
entity.entity_index,
|
||||||
|
entity.server_class,
|
||||||
|
send_table,
|
||||||
|
)?;
|
||||||
|
Self::write_update(
|
||||||
|
entity.diff_from_baseline(&baseline),
|
||||||
|
stream,
|
||||||
|
send_table,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
PVS::Preserve => {
|
||||||
|
Self::write_update(&entity.props, stream, send_table)?;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.delta.is_some() {
|
||||||
|
for removed in self.removed_entities.iter() {
|
||||||
|
removed.0.write_sized(stream, 11)?;
|
||||||
|
}
|
||||||
|
false.write(stream)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let length_end = stream.bit_len();
|
||||||
|
|
||||||
|
(length_end - length_start).write_sized(length_stream, 11)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PacketEntitiesMessage {
|
impl PacketEntitiesMessage {
|
||||||
fn read_enter(
|
fn read_enter(
|
||||||
stream: &mut Stream,
|
stream: &mut Stream,
|
||||||
|
|
@ -217,13 +349,7 @@ impl PacketEntitiesMessage {
|
||||||
.get(usize::from(class_index))
|
.get(usize::from(class_index))
|
||||||
.ok_or(ParseError::UnknownServerClass(class_index))?;
|
.ok_or(ParseError::UnknownServerClass(class_index))?;
|
||||||
|
|
||||||
let props = match state.instance_baselines[baseline_index].get(&entity_index) {
|
let props = state.get_baseline(baseline_index, entity_index, class_index, send_table)?;
|
||||||
Some(baseline) => baseline.clone(),
|
|
||||||
None => match state.static_baselines.get(&class_index) {
|
|
||||||
Some(_static_baseline) => state.get_static_baseline(class_index, send_table)?,
|
|
||||||
None => Vec::with_capacity(8),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(PacketEntity {
|
Ok(PacketEntity {
|
||||||
server_class: class_index,
|
server_class: class_index,
|
||||||
|
|
@ -236,6 +362,24 @@ impl PacketEntitiesMessage {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn write_enter(
|
||||||
|
entity: &PacketEntity,
|
||||||
|
stream: &mut BitWriteStream<LittleEndian>,
|
||||||
|
state: &ParserState,
|
||||||
|
) -> Result<()> {
|
||||||
|
let bits = log_base2(state.server_classes.len()) + 1;
|
||||||
|
let (class_index, _class) = state
|
||||||
|
.server_classes
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.find(|(_, class)| entity.server_class == class.id)
|
||||||
|
.ok_or(ParseError::UnknownServerClass(entity.server_class))?;
|
||||||
|
class_index.write_sized(stream, bits as usize)?;
|
||||||
|
entity.serial_number.write_sized(stream, 10)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn read_update(
|
pub fn read_update(
|
||||||
stream: &mut Stream,
|
stream: &mut Stream,
|
||||||
send_table: &SendTable,
|
send_table: &SendTable,
|
||||||
|
|
@ -266,6 +410,29 @@ impl PacketEntitiesMessage {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn write_update<'a, Props: IntoIterator<Item = &'a SendProp>>(
|
||||||
|
props: Props,
|
||||||
|
stream: &mut BitWriteStream<LittleEndian>,
|
||||||
|
send_table: &SendTable,
|
||||||
|
) -> Result<()> {
|
||||||
|
let mut last_index: i32 = -1;
|
||||||
|
|
||||||
|
for prop in props {
|
||||||
|
true.write(stream)?;
|
||||||
|
let (index, definition) = send_table
|
||||||
|
.flattened_props
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.find(|(_, def)| def.identifier == prop.index)
|
||||||
|
.ok_or(ParseError::UnknownDefinition(prop.index))?;
|
||||||
|
write_bit_var((index as i32 - last_index - 1) as u32, stream)?;
|
||||||
|
last_index = index as i32;
|
||||||
|
prop.value.encode(stream, &definition.parse_definition)?;
|
||||||
|
}
|
||||||
|
false.write(stream)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ParseBitSkip<'_> for PacketEntitiesMessage {
|
impl ParseBitSkip<'_> for PacketEntitiesMessage {
|
||||||
|
|
@ -281,3 +448,129 @@ impl ParseBitSkip<'_> for PacketEntitiesMessage {
|
||||||
.map_err(ParseError::from)
|
.map_err(ParseError::from)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_packet_entitier_message_roundtrip() {
|
||||||
|
use crate::demo::packet::datatable::{SendTable, SendTableName, ServerClass, ServerClassName};
|
||||||
|
|
||||||
|
let mut state = ParserState::new(|_| false, false);
|
||||||
|
state.server_classes = vec![
|
||||||
|
ServerClass {
|
||||||
|
id: ClassId::from(0),
|
||||||
|
name: ServerClassName::from("class1"),
|
||||||
|
data_table: SendTableName::from("table1"),
|
||||||
|
},
|
||||||
|
ServerClass {
|
||||||
|
id: ClassId::from(1),
|
||||||
|
name: ServerClassName::from("class2"),
|
||||||
|
data_table: SendTableName::from("table2"),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
state.send_tables = vec![
|
||||||
|
SendTable {
|
||||||
|
name: SendTableName::from("table1"),
|
||||||
|
needs_decoder: false,
|
||||||
|
raw_props: vec![],
|
||||||
|
flattened_props: vec![],
|
||||||
|
},
|
||||||
|
SendTable {
|
||||||
|
name: SendTableName::from("table2"),
|
||||||
|
needs_decoder: false,
|
||||||
|
raw_props: vec![],
|
||||||
|
flattened_props: vec![
|
||||||
|
SendPropDefinition {
|
||||||
|
identifier: SendPropIdentifier::new("table2", "prop1"),
|
||||||
|
parse_definition: SendPropParseDefinition::Int {
|
||||||
|
changes_often: false,
|
||||||
|
bit_count: 8,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
SendPropDefinition {
|
||||||
|
identifier: SendPropIdentifier::new("table2", "prop2"),
|
||||||
|
parse_definition: SendPropParseDefinition::String {
|
||||||
|
changes_often: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
SendPropDefinition {
|
||||||
|
identifier: SendPropIdentifier::new("table2", "prop3"),
|
||||||
|
parse_definition: SendPropParseDefinition::Float {
|
||||||
|
changes_often: false,
|
||||||
|
definition: FloatDefinition::Coord,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
state
|
||||||
|
.entity_classes
|
||||||
|
.insert(EntityId::from(4), ClassId::from(1));
|
||||||
|
crate::test_roundtrip_encode(
|
||||||
|
PacketEntitiesMessage {
|
||||||
|
entities: vec![],
|
||||||
|
removed_entities: vec![],
|
||||||
|
max_entries: 0,
|
||||||
|
delta: None,
|
||||||
|
base_line: 0,
|
||||||
|
updated_base_line: false,
|
||||||
|
},
|
||||||
|
&state,
|
||||||
|
);
|
||||||
|
crate::test_roundtrip_encode(
|
||||||
|
PacketEntitiesMessage {
|
||||||
|
entities: vec![PacketEntity {
|
||||||
|
server_class: ClassId::from(0),
|
||||||
|
entity_index: Default::default(),
|
||||||
|
props: vec![],
|
||||||
|
in_pvs: true,
|
||||||
|
pvs: PVS::Enter,
|
||||||
|
serial_number: 0,
|
||||||
|
delay: None,
|
||||||
|
}],
|
||||||
|
removed_entities: vec![],
|
||||||
|
max_entries: 4,
|
||||||
|
delta: None,
|
||||||
|
base_line: 0,
|
||||||
|
updated_base_line: false,
|
||||||
|
},
|
||||||
|
&state,
|
||||||
|
);
|
||||||
|
crate::test_roundtrip_encode(
|
||||||
|
PacketEntitiesMessage {
|
||||||
|
entities: vec![
|
||||||
|
PacketEntity {
|
||||||
|
server_class: ClassId::from(0),
|
||||||
|
entity_index: EntityId::from(0),
|
||||||
|
props: vec![],
|
||||||
|
in_pvs: true,
|
||||||
|
pvs: PVS::Enter,
|
||||||
|
serial_number: 0,
|
||||||
|
delay: None,
|
||||||
|
},
|
||||||
|
PacketEntity {
|
||||||
|
server_class: ClassId::from(1),
|
||||||
|
entity_index: EntityId::from(4),
|
||||||
|
props: vec![
|
||||||
|
SendProp {
|
||||||
|
index: SendPropIdentifier::new("table2", "prop1"),
|
||||||
|
value: SendPropValue::Integer(4),
|
||||||
|
},
|
||||||
|
SendProp {
|
||||||
|
index: SendPropIdentifier::new("table2", "prop3"),
|
||||||
|
value: SendPropValue::Float(1.0),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
in_pvs: false,
|
||||||
|
pvs: PVS::Preserve,
|
||||||
|
serial_number: 0,
|
||||||
|
delay: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
removed_entities: vec![],
|
||||||
|
max_entries: 4,
|
||||||
|
delta: None,
|
||||||
|
base_line: 0,
|
||||||
|
updated_base_line: false,
|
||||||
|
},
|
||||||
|
&state,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -263,7 +263,7 @@ impl Encode for UpdateStringTableMessage<'_> {
|
||||||
Some(table) => Ok(stream.reserve_length(20, |stream| {
|
Some(table) => Ok(stream.reserve_length(20, |stream| {
|
||||||
write_string_table_update(&self.entries, stream, table)
|
write_string_table_update(&self.entries, stream, table)
|
||||||
})?),
|
})?),
|
||||||
None => return Err(ParseError::StringTableNotFound(self.table_id)),
|
None => Err(ParseError::StringTableNotFound(self.table_id)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ use crate::demo::gamevent::GameEventValueType;
|
||||||
use crate::demo::message::gameevent::GameEventTypeId;
|
use crate::demo::message::gameevent::GameEventTypeId;
|
||||||
use crate::demo::message::packetentities::EntityId;
|
use crate::demo::message::packetentities::EntityId;
|
||||||
use crate::demo::packet::datatable::{ClassId, SendTableName};
|
use crate::demo::packet::datatable::{ClassId, SendTableName};
|
||||||
|
use crate::demo::sendprop::SendPropIdentifier;
|
||||||
use bitbuffer::BitError;
|
use bitbuffer::BitError;
|
||||||
use err_derive::Error;
|
use err_derive::Error;
|
||||||
use std::str::Utf8Error;
|
use std::str::Utf8Error;
|
||||||
|
|
@ -74,6 +75,8 @@ pub enum ParseError {
|
||||||
PropIndexOutOfBounds { index: i32, prop_count: usize },
|
PropIndexOutOfBounds { index: i32, prop_count: usize },
|
||||||
#[error(display = "An attempt was made to update an unknown entity: {}", _0)]
|
#[error(display = "An attempt was made to update an unknown entity: {}", _0)]
|
||||||
UnknownEntity(EntityId),
|
UnknownEntity(EntityId),
|
||||||
|
#[error(display = "No sendprop definition found for property")]
|
||||||
|
UnknownDefinition(SendPropIdentifier),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,22 @@ impl<'a> ParserState {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_baseline(
|
||||||
|
&self,
|
||||||
|
baseline_index: usize,
|
||||||
|
entity_index: EntityId,
|
||||||
|
class_id: ClassId,
|
||||||
|
send_table: &SendTable,
|
||||||
|
) -> Result<Vec<SendProp>> {
|
||||||
|
match self.instance_baselines[baseline_index].get(&entity_index) {
|
||||||
|
Some(baseline) => Ok(baseline.clone()),
|
||||||
|
None => match self.static_baselines.get(&class_id) {
|
||||||
|
Some(_static_baseline) => self.get_static_baseline(class_id, send_table),
|
||||||
|
None => Ok(Vec::with_capacity(8)),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn handle_data_table(
|
pub fn handle_data_table(
|
||||||
&mut self,
|
&mut self,
|
||||||
parse_tables: Vec<ParseSendTable>,
|
parse_tables: Vec<ParseSendTable>,
|
||||||
|
|
|
||||||
|
|
@ -1040,7 +1040,7 @@ impl SendPropIdentifier {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Display)]
|
#[derive(Debug, Clone, Display, PartialEq)]
|
||||||
#[display("{index} = {value}")]
|
#[display("{index} = {value}")]
|
||||||
pub struct SendProp {
|
pub struct SendProp {
|
||||||
pub index: SendPropIdentifier,
|
pub index: SendPropIdentifier,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue