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

strictly distinguish between server and demo ticks

This commit is contained in:
Robin Appelman 2022-09-17 15:31:32 +02:00
commit 28de17a67c
34 changed files with 14232 additions and 1033 deletions

View file

@ -5,6 +5,7 @@ use std::fs;
use bitbuffer::{BitError, BitRead, BitWrite, BitWriteStream, LittleEndian};
use main_error::MainError;
use tf_demo_parser::demo::data::DemoTick;
use tf_demo_parser::demo::header::Header;
use tf_demo_parser::demo::message::{setconvar::SetConVarMessage, Message, NetTickMessage};
use tf_demo_parser::demo::packet::stop::StopPacket;
@ -58,7 +59,7 @@ fn main() -> Result<(), MainError> {
let mut packet_start = packets.pos();
let mut has_stop = false;
let mut last_tick = 0;
let mut last_tick = DemoTick::default();
while let Some(mut packet) = packets.next(&handler.state_handler)? {
let packet_end = packets.pos();
@ -116,7 +117,7 @@ fn header_fixup(header: &mut Header, mut packets: RawPacketStream) -> Result<(),
let mut tickrate = 66;
while let Some(packet) = packets.next(&handler.state_handler)? {
ticks = packet.tick();
ticks = packet.tick().into();
if let Packet::Signon(message_packet) = &packet {
for message in &message_packet.messages {

View file

@ -2,6 +2,7 @@ pub mod userinfo;
use bitbuffer::{BitRead, BitReadStream, BitWrite, BitWriteStream, Endianness};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::cmp::Ordering;
use std::fmt::{Debug, Display, Formatter};
pub use userinfo::UserInfo;
@ -127,3 +128,87 @@ impl schemars::JsonSchema for MaybeUtf8String {
String::json_schema(gen)
}
}
/// Tick relative to the start of the game on the server
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(
Debug,
Clone,
Copy,
Ord,
PartialOrd,
Eq,
PartialEq,
BitRead,
BitWrite,
Serialize,
Deserialize,
Default,
)]
pub struct ServerTick(u32);
impl PartialEq<u32> for ServerTick {
fn eq(&self, other: &u32) -> bool {
*other == self.0
}
}
impl PartialOrd<u32> for ServerTick {
fn partial_cmp(&self, other: &u32) -> Option<Ordering> {
other.partial_cmp(&self.0)
}
}
impl From<u32> for ServerTick {
fn from(tick: u32) -> Self {
ServerTick(tick)
}
}
impl From<ServerTick> for u32 {
fn from(tick: ServerTick) -> Self {
tick.0
}
}
/// Tick relative to the start of the demo
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(
Debug,
Clone,
Copy,
Ord,
PartialOrd,
Eq,
PartialEq,
BitRead,
BitWrite,
Serialize,
Deserialize,
Default,
)]
pub struct DemoTick(u32);
impl PartialEq<u32> for DemoTick {
fn eq(&self, other: &u32) -> bool {
*other == self.0
}
}
impl PartialOrd<u32> for DemoTick {
fn partial_cmp(&self, other: &u32) -> Option<Ordering> {
other.partial_cmp(&self.0)
}
}
impl From<u32> for DemoTick {
fn from(tick: u32) -> Self {
DemoTick(tick)
}
}
impl From<DemoTick> for u32 {
fn from(tick: DemoTick) -> Self {
tick.0
}
}

View file

@ -1,5 +1,5 @@
/// Messages that consists only of primitives and string and can be derived
use crate::demo::data::MaybeUtf8String;
use crate::demo::data::{MaybeUtf8String, ServerTick};
use crate::Stream;
use bitbuffer::{BitRead, BitWrite, LittleEndian};
use serde::{Deserialize, Serialize};
@ -15,7 +15,7 @@ pub struct FileMessage {
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(BitRead, BitWrite, Debug, PartialEq, Serialize, Deserialize, Clone)]
pub struct NetTickMessage {
pub tick: u32,
pub tick: ServerTick,
pub frame_time: u16,
pub std_dev: u16,
}

View file

@ -1,3 +1,4 @@
use crate::demo::data::DemoTick;
use crate::{ReadResult, Stream};
use bitbuffer::{BitRead, BitWrite, LittleEndian};
use serde::{Deserialize, Serialize};
@ -5,13 +6,13 @@ use serde::{Deserialize, Serialize};
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
pub struct ConsoleCmdPacket {
pub tick: u32,
pub tick: DemoTick,
pub command: String,
}
impl BitRead<'_, LittleEndian> for ConsoleCmdPacket {
fn read(stream: &mut Stream) -> ReadResult<Self> {
let tick = stream.read_int(32)?;
let tick = stream.read()?;
let len = stream.read_int::<usize>(32)?;
let mut packet_data = stream.read_bits(len * 8)?;
let command: String = packet_data.read()?;

View file

@ -1,3 +1,4 @@
use crate::demo::data::DemoTick;
use crate::demo::parser::MalformedSendPropDefinitionError;
use crate::demo::sendprop::{
RawSendPropDefinition, SendPropDefinition, SendPropFlag, SendPropIdentifier, SendPropType,
@ -403,7 +404,7 @@ pub struct SendTable {
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
pub struct DataTablePacket {
pub tick: u32,
pub tick: DemoTick,
pub tables: Vec<ParseSendTable>,
pub server_classes: Vec<ServerClass>,
}
@ -460,7 +461,7 @@ fn test_data_table_packet_roundtrip() {
let state = ParserState::new(24, |_| false, false);
crate::test_roundtrip_encode(
DataTablePacket {
tick: 123,
tick: 123.into(),
tables: vec![],
server_classes: vec![],
},
@ -545,7 +546,7 @@ fn test_data_table_packet_roundtrip() {
};
crate::test_roundtrip_encode(
DataTablePacket {
tick: 1,
tick: 1.into(),
tables: vec![table1, table2],
server_classes: vec![
ServerClass {

View file

@ -1,6 +1,7 @@
use bitbuffer::{BitRead, BitWrite, BitWriteStream, LittleEndian};
use serde::{Deserialize, Serialize};
use crate::demo::data::DemoTick;
use crate::demo::message::{Message, MessageType};
use crate::demo::parser::Encode;
use crate::demo::vector::Vector;
@ -21,7 +22,7 @@ pub struct MessagePacketMeta {
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone, Default)]
#[serde(bound(deserialize = "'a: 'static"))]
pub struct MessagePacket<'a> {
pub tick: u32,
pub tick: DemoTick,
pub messages: Vec<Message<'a>>,
pub meta: MessagePacketMeta,
}

View file

@ -9,6 +9,7 @@ use self::stop::StopPacket;
use self::stringtable::StringTablePacket;
use self::synctick::SyncTickPacket;
use self::usercmd::UserCmdPacket;
use crate::demo::data::DemoTick;
use crate::demo::parser::Encode;
use serde::{Deserialize, Serialize};
#[cfg(feature = "trace")]
@ -38,7 +39,7 @@ pub enum Packet<'a> {
}
impl Packet<'_> {
pub fn tick(&self) -> u32 {
pub fn tick(&self) -> DemoTick {
match self {
Packet::Signon(msg) => msg.tick,
Packet::Message(msg) => msg.tick,
@ -51,7 +52,7 @@ impl Packet<'_> {
}
}
pub fn set_tick(&mut self, tick: u32) {
pub fn set_tick(&mut self, tick: DemoTick) {
match self {
Packet::Signon(msg) => msg.tick = tick,
Packet::Message(msg) => msg.tick = tick,

View file

@ -1,9 +1,23 @@
use bitbuffer::{BitRead, BitWrite};
use crate::demo::data::DemoTick;
use bitbuffer::{BitRead, BitReadStream, BitWrite, BitWriteStream, Endianness};
use serde::{Deserialize, Serialize};
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, BitRead, BitWrite, PartialEq, Serialize, Deserialize, Clone)]
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
pub struct StopPacket {
#[size = 24]
pub tick: u32,
pub tick: DemoTick,
}
impl<'a, E: Endianness> BitRead<'a, E> for StopPacket {
fn read(stream: &mut BitReadStream<'a, E>) -> bitbuffer::Result<Self> {
Ok(StopPacket {
tick: stream.read_int::<u32>(24)?.into(),
})
}
}
impl<E: Endianness> BitWrite<E> for StopPacket {
fn write(&self, stream: &mut BitWriteStream<E>) -> bitbuffer::Result<()> {
stream.write_int::<u32>(self.tick.into(), 24)
}
}

View file

@ -2,6 +2,7 @@ use bitbuffer::{BitRead, BitWrite, BitWriteStream, LittleEndian};
use serde::{Deserialize, Serialize};
use std::fmt;
use crate::demo::data::DemoTick;
use crate::demo::message::stringtable::StringTableMeta;
use crate::demo::parser::Encode;
use crate::{Parse, ParseError, ParserState, ReadResult, Result, Stream};
@ -233,13 +234,13 @@ impl fmt::Debug for StringTableEntry<'_> {
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
#[serde(bound(deserialize = "'a: 'static"))]
pub struct StringTablePacket<'a> {
pub tick: u32,
pub tick: DemoTick,
pub tables: Vec<StringTable<'a>>,
}
impl<'a> Parse<'a> for StringTablePacket<'a> {
fn parse(stream: &mut Stream<'a>, _state: &ParserState) -> Result<Self> {
let tick = stream.read_int(32)?;
let tick = stream.read()?;
let length: usize = stream.read_int(32)?;
let mut packet_data = stream.read_bits(length.saturating_mul(8))?;
let count: usize = packet_data.read_int(8)?;
@ -274,14 +275,14 @@ fn test_string_table_packet_roundtrip() {
let state = ParserState::new(24, |_| false, false);
crate::test_roundtrip_encode(
StringTablePacket {
tick: 1,
tick: 1.into(),
tables: vec![],
},
&state,
);
crate::test_roundtrip_encode(
StringTablePacket {
tick: 1,
tick: 1.into(),
tables: vec![StringTable {
name: "table1".into(),
entries: vec![],
@ -295,7 +296,7 @@ fn test_string_table_packet_roundtrip() {
);
crate::test_roundtrip_encode(
StringTablePacket {
tick: 1,
tick: 1.into(),
tables: vec![
StringTable {
name: "table1".into(),

View file

@ -1,8 +1,9 @@
use crate::demo::data::DemoTick;
use bitbuffer::{BitRead, BitWrite};
use serde::{Deserialize, Serialize};
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(BitRead, BitWrite, Debug, PartialEq, Serialize, Deserialize, Clone)]
pub struct SyncTickPacket {
pub tick: u32,
pub tick: DemoTick,
}

View file

@ -1,10 +1,11 @@
use crate::demo::data::DemoTick;
use bitbuffer::{BitRead, BitReadStream, BitWrite, BitWriteStream, LittleEndian};
use serde::{Deserialize, Serialize};
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
pub struct UserCmdPacket {
pub tick: u32,
pub tick: DemoTick,
pub sequence_out: u32,
pub cmd: UserCmd,
}

View file

@ -1,3 +1,4 @@
use crate::demo::data::{DemoTick, ServerTick};
use crate::demo::gameevent_gen::{
GameEvent, PlayerDeathEvent, PlayerSpawnEvent, TeamPlayRoundWinEvent,
};
@ -22,11 +23,11 @@ pub struct ChatMessage {
pub kind: ChatMessageKind,
pub from: String,
pub text: String,
pub tick: u32,
pub tick: DemoTick,
}
impl ChatMessage {
pub fn from_message(message: &SayText2Message, tick: u32) -> Self {
pub fn from_message(message: &SayText2Message, tick: DemoTick) -> Self {
ChatMessage {
kind: message.kind,
from: message
@ -264,11 +265,11 @@ pub struct Spawn {
pub user: UserId,
pub class: Class,
pub team: Team,
pub tick: u32,
pub tick: DemoTick,
}
impl Spawn {
pub fn from_event(event: &PlayerSpawnEvent, tick: u32) -> Self {
pub fn from_event(event: &PlayerSpawnEvent, tick: DemoTick) -> Self {
Spawn {
user: UserId::from(event.user_id),
class: Class::new(event.class),
@ -319,11 +320,11 @@ pub struct Death {
pub victim: UserId,
pub assister: Option<UserId>,
pub killer: UserId,
pub tick: u32,
pub tick: DemoTick,
}
impl Death {
pub fn from_event(event: &PlayerDeathEvent, tick: u32) -> Self {
pub fn from_event(event: &PlayerDeathEvent, tick: DemoTick) -> Self {
let assister = if event.assister < (16 * 1024) {
Some(UserId::from(event.assister))
} else {
@ -343,11 +344,11 @@ impl Death {
pub struct Round {
pub winner: Team,
pub length: f32,
pub end_tick: u32,
pub end_tick: DemoTick,
}
impl Round {
pub fn from_event(event: &TeamPlayRoundWinEvent, tick: u32) -> Self {
pub fn from_event(event: &TeamPlayRoundWinEvent, tick: DemoTick) -> Self {
Round {
winner: Team::new(event.team),
length: event.round_time,
@ -374,15 +375,20 @@ impl MessageHandler for Analyser {
fn does_handle(message_type: MessageType) -> bool {
matches!(
message_type,
MessageType::GameEvent | MessageType::UserMessage | MessageType::ServerInfo
MessageType::GameEvent
| MessageType::UserMessage
| MessageType::ServerInfo
| MessageType::NetTick
)
}
fn handle_message(&mut self, message: &Message, tick: u32, _parser_state: &ParserState) {
if self.state.start_tick == 0 {
self.state.start_tick = tick;
}
fn handle_message(&mut self, message: &Message, tick: DemoTick, _parser_state: &ParserState) {
match message {
Message::NetTick(msg) => {
if self.state.start_tick == 0 {
self.state.start_tick = msg.tick;
}
}
Message::ServerInfo(message) => {
self.state.interval_per_tick = message.interval_per_tick
}
@ -424,7 +430,7 @@ impl Analyser {
Self::default()
}
fn handle_user_message(&mut self, message: &UserMessage, tick: u32) {
fn handle_user_message(&mut self, message: &UserMessage, tick: DemoTick) {
if let UserMessage::SayText2(text_message) = message {
if text_message.kind == ChatMessageKind::NameChange {
if let Some(from) = text_message.from.clone() {
@ -444,7 +450,7 @@ impl Analyser {
}
}
fn handle_event(&mut self, event: &GameEvent, tick: u32) {
fn handle_event(&mut self, event: &GameEvent, tick: DemoTick) {
const WIN_REASON_TIME_LIMIT: u8 = 6;
match event {
@ -494,6 +500,6 @@ pub struct MatchState {
pub users: BTreeMap<UserId, UserInfo>,
pub deaths: Vec<Death>,
pub rounds: Vec<Round>,
pub start_tick: u32,
pub start_tick: ServerTick,
pub interval_per_tick: f32,
}

View file

@ -1,3 +1,4 @@
use crate::demo::data::DemoTick;
use crate::demo::gameevent_gen::{ObjectDestroyedEvent, PlayerDeathEvent};
use crate::demo::gamevent::GameEvent;
use crate::demo::message::gameevent::GameEventMessage;
@ -239,11 +240,11 @@ pub struct Kill {
pub assister_id: u16,
pub victim_id: u16,
pub weapon: String,
pub tick: u32,
pub tick: DemoTick,
}
impl Kill {
fn new(tick: u32, death: &PlayerDeathEvent) -> Self {
fn new(tick: DemoTick, death: &PlayerDeathEvent) -> Self {
Kill {
attacker_id: death.attacker,
assister_id: death.assister,
@ -260,7 +261,7 @@ pub struct GameState {
pub buildings: BTreeMap<EntityId, Building>,
pub world: Option<World>,
pub kills: Vec<Kill>,
pub tick: u32,
pub tick: DemoTick,
}
impl GameState {
@ -303,7 +304,7 @@ impl GameState {
#[derive(Default, Debug)]
pub struct GameStateAnalyser {
pub state: GameState,
tick: u32,
tick: DemoTick,
class_names: Vec<ServerClassName>, // indexed by ClassId
}
@ -317,7 +318,7 @@ impl MessageHandler for GameStateAnalyser {
)
}
fn handle_message(&mut self, message: &Message, _tick: u32, parser_state: &ParserState) {
fn handle_message(&mut self, message: &Message, _tick: DemoTick, parser_state: &ParserState) {
match message {
Message::PacketEntities(message) => {
for entity in &message.entities {
@ -374,7 +375,7 @@ impl MessageHandler for GameStateAnalyser {
fn handle_packet_meta(
&mut self,
tick: u32,
tick: DemoTick,
_meta: &MessagePacketMeta,
_parser_state: &ParserState,
) {

View file

@ -4,6 +4,7 @@ use crate::demo::packet::stringtable::{StringTable, StringTableEntry};
use crate::demo::packet::Packet;
use crate::Result;
use crate::demo::data::{DemoTick, ServerTick};
use crate::demo::header::Header;
use crate::demo::packet::message::MessagePacketMeta;
use crate::ParserState;
@ -16,7 +17,8 @@ pub trait MessageHandler {
fn handle_header(&mut self, _header: &Header) {}
fn handle_message(&mut self, _message: &Message, _tick: u32, _parser_state: &ParserState) {}
fn handle_message(&mut self, _message: &Message, _tick: DemoTick, _parser_state: &ParserState) {
}
fn handle_string_entry(
&mut self,
@ -37,7 +39,7 @@ pub trait MessageHandler {
fn handle_packet_meta(
&mut self,
_tick: u32,
_tick: DemoTick,
_meta: &MessagePacketMeta,
_parser_state: &ParserState,
) {
@ -64,7 +66,8 @@ impl MessageHandler for NullHandler {
#[derive(Clone)]
pub struct DemoHandler<'a, T: MessageHandler> {
pub tick: u32,
pub server_tick: ServerTick,
pub demo_tick: DemoTick,
pub string_table_names: Vec<Cow<'a, str>>,
analyser: T,
pub state_handler: ParserState,
@ -87,7 +90,8 @@ impl<'a, T: MessageHandler> DemoHandler<'a, T> {
let state_handler = ParserState::new(24, T::does_handle, false);
DemoHandler {
tick: 0,
server_tick: ServerTick::default(),
demo_tick: DemoTick::default(),
string_table_names: Vec::new(),
analyser,
state_handler,
@ -97,7 +101,8 @@ impl<'a, T: MessageHandler> DemoHandler<'a, T> {
let state_handler = ParserState::new(24, T::does_handle, true);
DemoHandler {
tick: 0,
server_tick: ServerTick::default(),
demo_tick: DemoTick::default(),
string_table_names: Vec::new(),
analyser,
state_handler,
@ -124,7 +129,10 @@ impl<'a, T: MessageHandler> DemoHandler<'a, T> {
.handle_packet_meta(packet.tick, &packet.meta, &self.state_handler);
for message in packet.messages {
match message {
Message::NetTick(message) => self.tick = message.tick,
Message::NetTick(message) => {
self.server_tick = message.tick;
self.handle_message(Message::NetTick(message), packet.tick)
}
Message::CreateStringTable(message) => {
self.handle_string_table(message.table)
}
@ -132,9 +140,9 @@ impl<'a, T: MessageHandler> DemoHandler<'a, T> {
self.handle_table_update(message.table_id, message.entries)
}
Message::PacketEntities(msg) => {
self.handle_message(Message::PacketEntities(msg))
self.handle_message(Message::PacketEntities(msg), packet.tick)
}
message => self.handle_message(message),
message => self.handle_message(message, packet.tick),
}
}
}
@ -184,13 +192,13 @@ impl<'a, T: MessageHandler> DemoHandler<'a, T> {
.handle_data_table(send_tables, server_classes)
}
pub fn handle_message(&mut self, message: Message<'a>) {
pub fn handle_message(&mut self, message: Message<'a>, tick: DemoTick) {
let message_type = message.get_message_type();
if T::does_handle(message_type) {
self.analyser
.handle_message(&message, self.tick, &self.state_handler);
.handle_message(&message, tick, &self.state_handler);
}
self.state_handler.handle_message(message, self.tick);
self.state_handler.handle_message(message, tick);
}
pub fn into_output(self) -> T::Output {

View file

@ -1,3 +1,4 @@
use crate::demo::data::DemoTick;
use crate::demo::message::{Message, MessageType};
use crate::demo::parser::handler::MessageHandler;
@ -16,7 +17,7 @@ impl MessageHandler for MessageTypeAnalyser {
true
}
fn handle_message(&mut self, message: &Message, _tick: u32, _parser_state: &ParserState) {
fn handle_message(&mut self, message: &Message, _tick: DemoTick, _parser_state: &ParserState) {
self.packet_types.push(message.get_message_type())
}

View file

@ -1,3 +1,4 @@
use crate::demo::data::DemoTick;
use bitbuffer::{BitError, BitRead, BitWrite, BitWriteStream, LittleEndian};
pub use self::messagetypeanalyser::MessageTypeAnalyser;
@ -208,5 +209,5 @@ impl<'a, A: MessageHandler + BorrowMessageHandler> DemoTicker<'a, A> {
pub struct Tick<'a, State> {
pub state: &'a State,
pub parser_state: &'a ParserState,
pub tick: u32,
pub tick: DemoTick,
}

View file

@ -14,6 +14,7 @@ use crate::demo::packet::datatable::{
};
use crate::demo::packet::stringtable::StringTableEntry;
use crate::demo::data::DemoTick;
use crate::demo::sendprop::{SendProp, SendPropIdentifier};
use crate::nullhasher::NullHasherBuilder;
use crate::{Result, Stream};
@ -221,7 +222,7 @@ impl<'a> ParserState {
)
}
pub fn handle_message(&mut self, message: Message, _tick: u32) {
pub fn handle_message(&mut self, message: Message, _tick: DemoTick) {
match message {
Message::ServerInfo(message) => {
self.demo_meta.version = message.version;