mirror of
https://codeberg.org/demostf/parser.git
synced 2026-06-03 10:14:06 +02:00
remove some bounds checks
This commit is contained in:
parent
b328db85e9
commit
6ef2b1cea8
9 changed files with 102 additions and 93 deletions
|
|
@ -6,6 +6,7 @@ use bitbuffer::{BitRead, BitWrite, BitWriteStream, LittleEndian};
|
|||
use parse_display::Display;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::cmp::Ordering;
|
||||
use const_fnv1a_hash::fnv1a_hash_str_64;
|
||||
|
||||
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
|
|
@ -17,7 +18,8 @@ pub struct GameEventDefinition {
|
|||
|
||||
impl GameEventDefinition {
|
||||
pub fn get_entry(&self, name: &str) -> Option<&GameEventEntry> {
|
||||
self.entries.iter().find(|entry| entry.name == name)
|
||||
let hash = fnv1a_hash_str_64(name);
|
||||
self.entries.iter().find(|entry| entry.hash == hash)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -45,9 +47,21 @@ impl Ord for GameEventDefinition {
|
|||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct GameEventEntry {
|
||||
pub name: String,
|
||||
pub hash: u64,
|
||||
pub kind: GameEventValueType,
|
||||
}
|
||||
|
||||
impl GameEventEntry {
|
||||
pub fn new<S: Into<String>>(name: S, kind: GameEventValueType) -> Self {
|
||||
let name = name.into();
|
||||
GameEventEntry {
|
||||
hash: fnv1a_hash_str_64(&name),
|
||||
name: name.into(),
|
||||
kind,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||
#[derive(BitRead, BitWrite, Debug, Clone, Copy, PartialEq, Display, Serialize, Deserialize)]
|
||||
#[discriminant_bits = 3]
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use bitbuffer::{BitRead, BitWrite, BitWriteSized, BitWriteStream, LittleEndian};
|
||||
use parse_display::Display;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::borrow::Cow;
|
||||
|
||||
use crate::demo::gameevent_gen::GameEventType;
|
||||
use crate::demo::gamevent::{
|
||||
|
|
@ -75,18 +76,15 @@ fn test_game_event_roundtrip() {
|
|||
GameEventDefinition {
|
||||
id: GameEventTypeId(0),
|
||||
event_type: GameEventType::ServerShutdown,
|
||||
entries: vec![GameEventEntry {
|
||||
name: "reason".to_string(),
|
||||
kind: GameEventValueType::String,
|
||||
}],
|
||||
entries: vec![GameEventEntry::new("reason", GameEventValueType::String)],
|
||||
},
|
||||
GameEventDefinition {
|
||||
id: GameEventTypeId(1),
|
||||
event_type: GameEventType::ServerChangeLevelFailed,
|
||||
entries: vec![GameEventEntry {
|
||||
name: "level_name".to_string(),
|
||||
kind: GameEventValueType::String,
|
||||
}],
|
||||
entries: vec![GameEventEntry::new(
|
||||
"level_name",
|
||||
GameEventValueType::String,
|
||||
)],
|
||||
},
|
||||
GameEventDefinition {
|
||||
id: GameEventTypeId(2),
|
||||
|
|
@ -168,11 +166,8 @@ impl BitRead<'_, LittleEndian> for GameEventDefinition {
|
|||
|
||||
let mut entry_type = stream.read()?;
|
||||
while entry_type != GameEventValueType::None {
|
||||
let entry_name = stream.read()?;
|
||||
entries.push(GameEventEntry {
|
||||
name: entry_name,
|
||||
kind: entry_type,
|
||||
});
|
||||
let entry_name: Cow<str> = stream.read()?;
|
||||
entries.push(GameEventEntry::new(entry_name, entry_type));
|
||||
entry_type = stream.read()?;
|
||||
}
|
||||
|
||||
|
|
@ -207,10 +202,10 @@ fn test_event_definition_roundtrip() {
|
|||
crate::test_roundtrip_write(GameEventDefinition {
|
||||
id: GameEventTypeId(0),
|
||||
event_type: GameEventType::ServerChangeLevelFailed,
|
||||
entries: vec![GameEventEntry {
|
||||
name: "level_name".to_string(),
|
||||
kind: GameEventValueType::String,
|
||||
}],
|
||||
entries: vec![GameEventEntry::new(
|
||||
"level_name",
|
||||
GameEventValueType::String,
|
||||
)],
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -244,10 +239,7 @@ fn test_event_list_roundtrip() {
|
|||
event_list: vec![GameEventDefinition {
|
||||
id: GameEventTypeId(0),
|
||||
event_type: GameEventType::ServerChangeLevelFailed,
|
||||
entries: vec![GameEventEntry {
|
||||
name: "level_name".to_string(),
|
||||
kind: GameEventValueType::String,
|
||||
}],
|
||||
entries: vec![GameEventEntry::new("level_name", GameEventValueType::String)],
|
||||
}],
|
||||
});
|
||||
crate::test_roundtrip_write(GameEventListMessage {
|
||||
|
|
@ -256,55 +248,25 @@ fn test_event_list_roundtrip() {
|
|||
id: GameEventTypeId(0),
|
||||
event_type: GameEventType::ServerSpawn,
|
||||
entries: vec![
|
||||
GameEventEntry {
|
||||
name: "hostname".to_string(),
|
||||
kind: GameEventValueType::String,
|
||||
},
|
||||
GameEventEntry {
|
||||
name: "address".to_string(),
|
||||
kind: GameEventValueType::String,
|
||||
},
|
||||
GameEventEntry {
|
||||
name: "ip".to_string(),
|
||||
kind: GameEventValueType::Long,
|
||||
},
|
||||
GameEventEntry {
|
||||
name: "port".to_string(),
|
||||
kind: GameEventValueType::Short,
|
||||
},
|
||||
GameEventEntry {
|
||||
name: "game".to_string(),
|
||||
kind: GameEventValueType::String,
|
||||
},
|
||||
GameEventEntry {
|
||||
name: "map_name".to_string(),
|
||||
kind: GameEventValueType::String,
|
||||
},
|
||||
GameEventEntry {
|
||||
name: "max_players".to_string(),
|
||||
kind: GameEventValueType::Long,
|
||||
},
|
||||
GameEventEntry {
|
||||
name: "os".to_string(),
|
||||
kind: GameEventValueType::String,
|
||||
},
|
||||
GameEventEntry {
|
||||
name: "dedicated".to_string(),
|
||||
kind: GameEventValueType::Boolean,
|
||||
},
|
||||
GameEventEntry {
|
||||
name: "password".to_string(),
|
||||
kind: GameEventValueType::Boolean,
|
||||
},
|
||||
GameEventEntry::new("hostname", GameEventValueType::String),
|
||||
GameEventEntry::new("address", GameEventValueType::String),
|
||||
GameEventEntry::new("ip", GameEventValueType::Long),
|
||||
GameEventEntry::new("port", GameEventValueType::Short),
|
||||
GameEventEntry::new("game", GameEventValueType::String),
|
||||
GameEventEntry::new("map_name", GameEventValueType::String),
|
||||
GameEventEntry::new("max_players", GameEventValueType::Long),
|
||||
GameEventEntry::new("os", GameEventValueType::String),
|
||||
GameEventEntry::new("dedicated", GameEventValueType::Boolean),
|
||||
GameEventEntry::new("password", GameEventValueType::Boolean),
|
||||
],
|
||||
},
|
||||
GameEventDefinition {
|
||||
id: GameEventTypeId(1),
|
||||
event_type: GameEventType::ServerChangeLevelFailed,
|
||||
entries: vec![GameEventEntry {
|
||||
name: "level_name".to_string(),
|
||||
kind: GameEventValueType::String,
|
||||
}],
|
||||
entries: vec![GameEventEntry::new(
|
||||
"level_name",
|
||||
GameEventValueType::String,
|
||||
)],
|
||||
},
|
||||
GameEventDefinition {
|
||||
id: GameEventTypeId(2),
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ 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};
|
||||
use crate::demo::parser::{Encode, ParseBitSkip};
|
||||
use crate::demo::sendprop::{SendProp, SendPropIdentifier, SendPropValue};
|
||||
|
|
@ -352,10 +351,21 @@ impl Parse<'_> for PacketEntitiesMessage {
|
|||
fn parse(stream: &mut Stream, state: &ParserState) -> Result<Self> {
|
||||
let max_entries = stream.read_sized(11)?;
|
||||
let delta: Option<ServerTick> = stream.read()?;
|
||||
let base_line = stream.read()?;
|
||||
let updated_entries: u16 = stream.read_sized(11)?;
|
||||
let length: u32 = stream.read_sized(20)?;
|
||||
let updated_base_line = stream.read()?;
|
||||
#[derive(BitRead)]
|
||||
struct Header {
|
||||
base_line: BaselineIndex,
|
||||
#[bitbuffer(size = 11)]
|
||||
updated_entries: u16,
|
||||
#[bitbuffer(size = 20)]
|
||||
length: u32,
|
||||
updated_base_line: bool,
|
||||
}
|
||||
let Header {
|
||||
base_line,
|
||||
updated_entries,
|
||||
length,
|
||||
updated_base_line,
|
||||
} = stream.read()?;
|
||||
|
||||
let mut data = stream.read_bits(length as usize)?;
|
||||
|
||||
|
|
@ -486,10 +496,16 @@ impl PacketEntitiesMessage {
|
|||
baseline_index: BaselineIndex,
|
||||
delta: Option<ServerTick>,
|
||||
) -> 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 bits = state.server_class_bits;
|
||||
#[derive(BitReadSized)]
|
||||
struct Data {
|
||||
#[bitbuffer(size = "input_size")]
|
||||
raw_index: u16,
|
||||
#[bitbuffer(size = 10)]
|
||||
serial: u32,
|
||||
}
|
||||
let Data { raw_index, serial } = stream.read_sized(bits)?;
|
||||
let class_index: ClassId = raw_index.into();
|
||||
|
||||
Ok(PacketEntity {
|
||||
server_class: class_index,
|
||||
|
|
@ -509,8 +525,7 @@ impl PacketEntitiesMessage {
|
|||
stream: &mut BitWriteStream<LittleEndian>,
|
||||
state: &ParserState,
|
||||
) -> Result<()> {
|
||||
let bits = log_base2(state.server_classes.len()) + 1;
|
||||
u16::from(entity.server_class).write_sized(stream, bits as usize)?;
|
||||
u16::from(entity.server_class).write_sized(stream, state.server_class_bits)?;
|
||||
entity.serial_number.write_sized(stream, 10)?;
|
||||
|
||||
Ok(())
|
||||
|
|
@ -613,12 +628,12 @@ impl ParseBitSkip<'_> for PacketEntitiesMessage {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_packet_entitier_message_roundtrip() {
|
||||
fn test_packet_entity_message_roundtrip() {
|
||||
use crate::demo::packet::datatable::{SendTable, SendTableName, ServerClass, ServerClassName};
|
||||
use crate::demo::sendprop::{FloatDefinition, SendPropDefinition, SendPropParseDefinition};
|
||||
|
||||
let mut state = ParserState::new(24, |_| false, false);
|
||||
state.server_classes = vec![
|
||||
state.set_server_classes(vec![
|
||||
ServerClass {
|
||||
id: ClassId::from(0),
|
||||
name: ServerClassName::from("class1"),
|
||||
|
|
@ -629,7 +644,7 @@ fn test_packet_entitier_message_roundtrip() {
|
|||
name: ServerClassName::from("class2"),
|
||||
data_table: SendTableName::from("table2"),
|
||||
},
|
||||
];
|
||||
]);
|
||||
state.send_tables = vec![
|
||||
SendTable {
|
||||
name: SendTableName::from("table1"),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use super::stringtable::read_var_int;
|
||||
use crate::demo::message::packetentities::PacketEntitiesMessage;
|
||||
use crate::demo::message::stringtable::{encode_var_int_fixed, log_base2};
|
||||
use crate::demo::message::stringtable::{encode_var_int_fixed};
|
||||
use crate::demo::packet::datatable::ClassId;
|
||||
use crate::demo::parser::{Encode, ParseBitSkip};
|
||||
use crate::demo::sendprop::SendProp;
|
||||
|
|
@ -53,8 +53,8 @@ impl Parse<'_> for TempEntitiesMessage {
|
|||
};
|
||||
|
||||
let class_id = if stream.read()? {
|
||||
let bits = log_base2(state.server_classes.len()) + 1;
|
||||
(stream.read_sized::<u16>(bits as usize)?.saturating_sub(1)).into()
|
||||
let bits = state.server_class_bits;
|
||||
(stream.read_sized::<u16>(bits)?.saturating_sub(1)).into()
|
||||
} else {
|
||||
let last = events.last().ok_or(ParseError::InvalidDemo(
|
||||
"temp entity update without previous",
|
||||
|
|
@ -118,9 +118,9 @@ impl Encode for TempEntitiesMessage {
|
|||
|
||||
if event.class_id != last_class_id {
|
||||
true.write(stream)?;
|
||||
let bits = log_base2(state.server_classes.len()) + 1;
|
||||
let bits = state.server_class_bits;
|
||||
let id: u16 = event.class_id.into();
|
||||
(id + 1).write_sized(stream, bits as usize)?;
|
||||
(id + 1).write_sized(stream, bits)?;
|
||||
} else {
|
||||
false.write(stream)?;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,9 +4,7 @@ use crate::demo::sendprop::{
|
|||
RawSendPropDefinition, SendPropDefinition, SendPropFlag, SendPropIdentifier, SendPropType,
|
||||
};
|
||||
use crate::{Parse, ParseError, ParserState, Result, Stream};
|
||||
use bitbuffer::{
|
||||
BitRead, BitReadStream, BitWrite, BitWriteSized, BitWriteStream, Endianness, LittleEndian,
|
||||
};
|
||||
use bitbuffer::{BitRead, BitReadStream, BitWrite, BitWriteSized, BitWriteStream, Endianness, LittleEndian};
|
||||
use parse_display::{Display, FromStr};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::borrow::Cow;
|
||||
|
|
@ -133,7 +131,7 @@ impl SendTableName {
|
|||
|
||||
impl<E: Endianness> BitRead<'_, E> for SendTableName {
|
||||
fn read(stream: &mut BitReadStream<'_, E>) -> bitbuffer::Result<Self> {
|
||||
String::read(stream).map(SendTableName::from)
|
||||
<String as BitRead<'_, E>>::read(stream).map(SendTableName::from)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -185,6 +185,11 @@ impl GameStateAnalyser {
|
|||
}
|
||||
|
||||
pub fn handle_world_entity(&mut self, entity: &PacketEntity, parser_state: &ParserState) {
|
||||
const MINS: SendPropIdentifier =
|
||||
SendPropIdentifier::new("DT_WORLD", "m_WorldMins");
|
||||
const MAXS: SendPropIdentifier =
|
||||
SendPropIdentifier::new("DT_WORLD", "m_WorldMaxs");
|
||||
|
||||
if let (
|
||||
Some(SendProp {
|
||||
value: SendPropValue::Vector(boundary_min),
|
||||
|
|
@ -195,8 +200,8 @@ impl GameStateAnalyser {
|
|||
..
|
||||
}),
|
||||
) = (
|
||||
entity.get_prop_by_name("DT_WORLD", "m_WorldMins", parser_state),
|
||||
entity.get_prop_by_name("DT_WORLD", "m_WorldMaxs", parser_state),
|
||||
entity.get_prop_by_identifier(&MINS, parser_state),
|
||||
entity.get_prop_by_identifier(&MAXS, parser_state),
|
||||
) {
|
||||
self.state.world = Some(World {
|
||||
boundary_min,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use crate::demo::message::packetentities::{
|
|||
BaselineIndex, EntityId, PacketEntitiesMessage, PacketEntity, UpdateType,
|
||||
};
|
||||
use crate::demo::message::stringtable::StringTableMeta;
|
||||
use crate::demo::message::{Message, MessageType};
|
||||
use crate::demo::message::{log_base2, Message, MessageType};
|
||||
use crate::demo::packet::datatable::{
|
||||
ClassId, ParseSendTable, SendTable, SendTableName, ServerClass,
|
||||
};
|
||||
|
|
@ -40,6 +40,7 @@ pub struct ParserState {
|
|||
// indexed by ClassId
|
||||
pub send_tables: Vec<SendTable>,
|
||||
pub server_classes: Vec<ServerClass>,
|
||||
pub server_class_bits: usize,
|
||||
pub instance_baselines: [Baseline; 2],
|
||||
pub demo_meta: DemoMeta,
|
||||
analyser_handles: fn(message_type: MessageType) -> bool,
|
||||
|
|
@ -85,6 +86,7 @@ impl ParserState {
|
|||
entity_classes: HashMap::with_hasher(NullHasherBuilder),
|
||||
send_tables: Vec::new(),
|
||||
server_classes: Vec::new(),
|
||||
server_class_bits: 0,
|
||||
instance_baselines: [Baseline::default(), Baseline::default()],
|
||||
demo_meta: DemoMeta::default(),
|
||||
analyser_handles,
|
||||
|
|
@ -94,6 +96,11 @@ impl ParserState {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn set_server_classes(&mut self, server_classes: Vec<ServerClass>) {
|
||||
self.server_class_bits = log_base2(server_classes.len()) as usize + 1;
|
||||
self.server_classes = server_classes;
|
||||
}
|
||||
|
||||
pub fn get_static_baseline(
|
||||
&self,
|
||||
class_id: ClassId,
|
||||
|
|
@ -193,7 +200,7 @@ impl ParserState {
|
|||
})
|
||||
.collect::<Result<_>>()?;
|
||||
|
||||
self.server_classes = server_classes;
|
||||
self.set_server_classes(server_classes);
|
||||
|
||||
self.send_tables.reserve(self.server_classes.len());
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue