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

game event writing

This commit is contained in:
Robin Appelman 2021-07-18 15:03:26 +02:00
commit a55217cc55
11 changed files with 1021 additions and 844 deletions

File diff suppressed because it is too large Load diff

View file

@ -239,7 +239,7 @@ pub fn generate_game_events(demo: Demo) -> TokenStream {
quote!(pub #name: #ty,) quote!(pub #name: #ty,)
}); });
let name = Ident::new(&format!("{}Event", get_event_name(&event.name)), span); let name = Ident::new(&format!("{}Event", get_event_name(event.event_type.as_str())), span);
let entry_readers = event.entries.iter().map(|entry| { let entry_readers = event.entries.iter().map(|entry| {
let name_str = get_entry_name(&entry.name); let name_str = get_entry_name(&entry.name);
@ -260,7 +260,7 @@ pub fn generate_game_events(demo: Demo) -> TokenStream {
}; };
quote!( quote!(
#[derive(Debug, BitWrite)] #[derive(Debug, BitWrite, PartialEq)]
pub struct #name { pub struct #name {
#(#fields)* #(#fields)*
} }
@ -279,7 +279,7 @@ pub fn generate_game_events(demo: Demo) -> TokenStream {
}); });
let event_variants = events.iter().map(|event| { let event_variants = events.iter().map(|event| {
let name_str = get_event_name(&event.name); let name_str = get_event_name(event.event_type.as_str());
let name = Ident::new(&name_str, span); let name = Ident::new(&name_str, span);
let struct_name = Ident::new(&format!("{}Event", name_str), span); let struct_name = Ident::new(&format!("{}Event", name_str), span);
@ -291,7 +291,7 @@ pub fn generate_game_events(demo: Demo) -> TokenStream {
}); });
let event_types = events.iter().map(|event| { let event_types = events.iter().map(|event| {
let name_str = get_event_name(&event.name); let name_str = get_event_name(event.event_type.as_str());
let name = Ident::new(&name_str, span); let name = Ident::new(&name_str, span);
let id = Literal::u16_unsuffixed(event.id.into()); let id = Literal::u16_unsuffixed(event.id.into());
@ -299,21 +299,21 @@ pub fn generate_game_events(demo: Demo) -> TokenStream {
}); });
let type_from_names = events.iter().map(|event| { let type_from_names = events.iter().map(|event| {
let name_str = &event.name; let name_str = event.event_type.as_str();
let variant_name = Ident::new(&get_event_name(&name_str), span); let variant_name = Ident::new(&get_event_name(&name_str), span);
quote!(#name_str => GameEventType::#variant_name,) quote!(#name_str => GameEventType::#variant_name,)
}); });
let type_to_names = events.iter().map(|event| { let type_to_names = events.iter().map(|event| {
let name_str = &event.name; let name_str = event.event_type.as_str();
let variant_name = Ident::new(&get_event_name(&name_str), span); let variant_name = Ident::new(&get_event_name(&name_str), span);
quote!(GameEventType::#variant_name => #name_str,) quote!(GameEventType::#variant_name => #name_str,)
}); });
let read_events = events.iter().map(|event| { let read_events = events.iter().map(|event| {
let name = get_event_name(&event.name); let name = get_event_name(event.event_type.as_str());
let variant_name = Ident::new(&name, span); let variant_name = Ident::new(&name, span);
let struct_name = Ident::new(&format!("{}Event", name), span); let struct_name = Ident::new(&format!("{}Event", name), span);
@ -333,7 +333,7 @@ pub fn generate_game_events(demo: Demo) -> TokenStream {
}); });
let write_events = events.iter().map(|event| { let write_events = events.iter().map(|event| {
let name = get_event_name(&event.name); let name = get_event_name(event.event_type.as_str());
let variant_name = Ident::new(&name, span); let variant_name = Ident::new(&name, span);
quote!( quote!(
@ -342,7 +342,7 @@ pub fn generate_game_events(demo: Demo) -> TokenStream {
}); });
let sizes = events.iter().map(|event| { let sizes = events.iter().map(|event| {
let name = get_event_name(&event.name); let name = get_event_name(event.event_type.as_str());
let struct_name = Ident::new(&format!("{}Event", name), span); let struct_name = Ident::new(&format!("{}Event", name), span);
quote!( quote!(
@ -376,7 +376,7 @@ pub fn generate_game_events(demo: Demo) -> TokenStream {
#(#event_definitions)* #(#event_definitions)*
#[derive(Debug)] #[derive(Debug, PartialEq)]
pub enum GameEvent { pub enum GameEvent {
#(#event_variants)* #(#event_variants)*
Unknown(RawGameEvent), Unknown(RawGameEvent),

File diff suppressed because it is too large Load diff

View file

@ -12,7 +12,6 @@ use std::cmp::Ordering;
pub struct GameEventDefinition { pub struct GameEventDefinition {
pub id: GameEventTypeId, pub id: GameEventTypeId,
pub event_type: GameEventType, pub event_type: GameEventType,
pub name: String,
pub entries: Vec<GameEventEntry>, pub entries: Vec<GameEventEntry>,
} }
@ -36,7 +35,7 @@ impl Ord for GameEventDefinition {
} }
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq)]
pub struct GameEventEntry { pub struct GameEventEntry {
pub name: String, pub name: String,
pub kind: GameEventValueType, pub kind: GameEventValueType,
@ -55,7 +54,7 @@ pub enum GameEventValueType {
Local = 7, Local = 7,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq)]
pub enum GameEventValue { pub enum GameEventValue {
String(String), String(String),
Float(f32), Float(f32),
@ -155,7 +154,7 @@ impl EventValue for () {
} }
} }
#[derive(Debug)] #[derive(Debug, PartialEq)]
pub struct RawGameEvent { pub struct RawGameEvent {
pub event_type: GameEventType, pub event_type: GameEventType,
pub values: Vec<GameEventValue>, pub values: Vec<GameEventValue>,

View file

@ -75,14 +75,14 @@ impl BitWrite<LittleEndian> for BSPDecalMessage {
#[test] #[test]
fn test_decal_roundtrip() { fn test_decal_roundtrip() {
crate::test_roundtrip_encode(BSPDecalMessage { crate::test_roundtrip_write(BSPDecalMessage {
position: Vector::default(), position: Vector::default(),
texture_index: 0, texture_index: 0,
ent_index: 0, ent_index: 0,
model_index: 0, model_index: 0,
low_priority: false, low_priority: false,
}); });
crate::test_roundtrip_encode(BSPDecalMessage { crate::test_roundtrip_write(BSPDecalMessage {
position: Vector { position: Vector {
x: 1.0, x: 1.0,
y: 0.5, y: 0.5,

View file

@ -59,12 +59,12 @@ impl BitWrite<LittleEndian> for ClassInfoMessage {
#[test] #[test]
fn test_say_text2_roundtrip() { fn test_say_text2_roundtrip() {
crate::test_roundtrip_encode(ClassInfoMessage { crate::test_roundtrip_write(ClassInfoMessage {
count: 8, count: 8,
create: true, create: true,
entries: Vec::new(), entries: Vec::new(),
}); });
crate::test_roundtrip_encode(ClassInfoMessage { crate::test_roundtrip_write(ClassInfoMessage {
count: 3, count: 3,
create: false, create: false,
entries: vec![ entries: vec![

View file

@ -1,4 +1,4 @@
use bitbuffer::{BitRead, BitWrite, BitWriteStream, LittleEndian}; use bitbuffer::{BitRead, BitWrite, BitWriteSized, BitWriteStream, LittleEndian};
use parse_display::Display; use parse_display::Display;
use crate::demo::gameevent_gen::GameEventType; use crate::demo::gameevent_gen::GameEventType;
@ -8,8 +8,9 @@ use crate::demo::gamevent::{
use crate::demo::parser::{Encode, ParseBitSkip}; use crate::demo::parser::{Encode, ParseBitSkip};
use crate::{GameEventError, Parse, ParseError, ParserState, ReadResult, Result, Stream}; use crate::{GameEventError, Parse, ParseError, ParserState, ReadResult, Result, Stream};
#[derive(Debug)] #[derive(Debug, PartialEq)]
pub struct GameEventMessage { pub struct GameEventMessage {
pub event_type_id: GameEventTypeId,
pub event: GameEvent, pub event: GameEvent,
} }
@ -17,11 +18,12 @@ impl Parse<'_> for GameEventMessage {
fn parse(stream: &mut Stream, state: &ParserState) -> Result<Self> { fn parse(stream: &mut Stream, state: &ParserState) -> Result<Self> {
let length: u16 = stream.read_sized(11)?; let length: u16 = stream.read_sized(11)?;
let mut data = stream.read_bits(length as usize)?; let mut data = stream.read_bits(length as usize)?;
let event_type: GameEventTypeId = data.read()?; let event_type_id: GameEventTypeId = data.read()?;
// game event definitions haven't been sent yet, ignore // game event definitions haven't been sent yet, ignore
if state.event_definitions.is_empty() { if state.event_definitions.is_empty() {
return Ok(GameEventMessage { return Ok(GameEventMessage {
event_type_id,
event: GameEvent::Unknown(RawGameEvent { event: GameEvent::Unknown(RawGameEvent {
event_type: GameEventType::Unknown, event_type: GameEventType::Unknown,
values: Vec::new(), values: Vec::new(),
@ -29,15 +31,18 @@ impl Parse<'_> for GameEventMessage {
}); });
} }
let event = match state.event_definitions.get(usize::from(event_type)) { let event = match state.event_definitions.get(usize::from(event_type_id)) {
Some(definition) => GameEvent::read(&mut data, definition)?, Some(definition) => GameEvent::read(&mut data, definition)?,
None => { None => {
return Err(ParseError::MalformedGameEvent(GameEventError::UnknownType( return Err(ParseError::MalformedGameEvent(GameEventError::UnknownType(
event_type, event_type_id,
))); )));
} }
}; };
Ok(GameEventMessage { event }) Ok(GameEventMessage {
event_type_id,
event,
})
} }
} }
@ -47,10 +52,61 @@ impl Encode for GameEventMessage {
stream: &mut BitWriteStream<LittleEndian>, stream: &mut BitWriteStream<LittleEndian>,
_state: &ParserState, _state: &ParserState,
) -> Result<()> { ) -> Result<()> {
Ok(stream.reserve_length(11, |_stream| Ok(()))?) Ok(stream.reserve_length(11, |stream| {
self.event_type_id.write(stream)?;
self.event.write(stream)
})?)
} }
} }
#[test]
fn test_game_event_roundtrip() {
use crate::demo::gameevent_gen::{GameInitEvent, ServerShutdownEvent};
let definitions = vec![
GameEventDefinition {
id: GameEventTypeId(0),
event_type: GameEventType::ServerShutdown,
entries: vec![GameEventEntry {
name: "reason".to_string(),
kind: GameEventValueType::String,
}],
},
GameEventDefinition {
id: GameEventTypeId(1),
event_type: GameEventType::ServerChangeLevelFailed,
entries: vec![GameEventEntry {
name: "level_name".to_string(),
kind: GameEventValueType::String,
}],
},
GameEventDefinition {
id: GameEventTypeId(2),
event_type: GameEventType::GameInit,
entries: vec![],
},
];
let mut state = ParserState::new(|_| false, false);
state.event_definitions = definitions;
crate::test_roundtrip_encode(
GameEventMessage {
event_type_id: GameEventTypeId(0),
event: GameEvent::ServerShutdown(ServerShutdownEvent {
reason: "asd".into(),
}),
},
&state,
);
crate::test_roundtrip_encode(
GameEventMessage {
event_type_id: GameEventTypeId(2),
event: GameEvent::GameInit(GameInitEvent {}),
},
&state,
);
}
impl ParseBitSkip<'_> for GameEventMessage { impl ParseBitSkip<'_> for GameEventMessage {
fn parse_skip(stream: &mut Stream) -> Result<()> { fn parse_skip(stream: &mut Stream) -> Result<()> {
let length: u16 = stream.read_sized(11)?; let length: u16 = stream.read_sized(11)?;
@ -73,7 +129,7 @@ impl From<GameEventTypeId> for u16 {
} }
} }
#[derive(Debug)] #[derive(Debug, PartialEq)]
pub struct GameEventListMessage { pub struct GameEventListMessage {
pub event_list: Vec<GameEventDefinition>, pub event_list: Vec<GameEventDefinition>,
} }
@ -97,7 +153,6 @@ impl BitRead<'_, LittleEndian> for GameEventDefinition {
Ok(GameEventDefinition { Ok(GameEventDefinition {
id: event_type, id: event_type,
event_type: GameEventType::from_type_name(name.as_str()), event_type: GameEventType::from_type_name(name.as_str()),
name,
entries, entries,
}) })
} }
@ -107,7 +162,6 @@ impl BitWrite<LittleEndian> for GameEventDefinition {
fn write(&self, stream: &mut BitWriteStream<LittleEndian>) -> ReadResult<()> { fn write(&self, stream: &mut BitWriteStream<LittleEndian>) -> ReadResult<()> {
self.id.write(stream)?; self.id.write(stream)?;
self.event_type.as_str().write(stream)?; self.event_type.as_str().write(stream)?;
self.name.write(stream)?;
for entry in self.entries.iter() { for entry in self.entries.iter() {
entry.kind.write(stream)?; entry.kind.write(stream)?;
@ -119,6 +173,18 @@ impl BitWrite<LittleEndian> for GameEventDefinition {
} }
} }
#[test]
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,
}],
});
}
impl BitRead<'_, LittleEndian> for GameEventListMessage { impl BitRead<'_, LittleEndian> for GameEventListMessage {
fn read(stream: &mut Stream) -> ReadResult<Self> { fn read(stream: &mut Stream) -> ReadResult<Self> {
let count: u16 = stream.read_sized(9)?; let count: u16 = stream.read_sized(9)?;
@ -132,7 +198,7 @@ impl BitRead<'_, LittleEndian> for GameEventListMessage {
impl BitWrite<LittleEndian> for GameEventListMessage { impl BitWrite<LittleEndian> for GameEventListMessage {
fn write(&self, stream: &mut BitWriteStream<LittleEndian>) -> ReadResult<()> { fn write(&self, stream: &mut BitWriteStream<LittleEndian>) -> ReadResult<()> {
(self.event_list.len() as u16).write(stream)?; (self.event_list.len() as u16).write_sized(stream, 9)?;
stream.reserve_length(20, |stream| { stream.reserve_length(20, |stream| {
for event in self.event_list.iter() { for event in self.event_list.iter() {
event.write(stream)?; event.write(stream)?;
@ -143,3 +209,81 @@ impl BitWrite<LittleEndian> for GameEventListMessage {
Ok(()) Ok(())
} }
} }
#[test]
fn test_event_list_roundtrip() {
crate::test_roundtrip_write(GameEventListMessage { event_list: vec![] });
crate::test_roundtrip_write(GameEventListMessage {
event_list: vec![GameEventDefinition {
id: GameEventTypeId(0),
event_type: GameEventType::ServerChangeLevelFailed,
entries: vec![GameEventEntry {
name: "level_name".to_string(),
kind: GameEventValueType::String,
}],
}],
});
crate::test_roundtrip_write(GameEventListMessage {
event_list: vec![
GameEventDefinition {
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,
},
],
},
GameEventDefinition {
id: GameEventTypeId(1),
event_type: GameEventType::ServerChangeLevelFailed,
entries: vec![GameEventEntry {
name: "level_name".to_string(),
kind: GameEventValueType::String,
}],
},
GameEventDefinition {
id: GameEventTypeId(2),
event_type: GameEventType::GameInit,
entries: vec![],
},
],
});
}

View file

@ -140,8 +140,8 @@ impl<'a> BitWrite<LittleEndian> for UserMessage<'a> {
#[test] #[test]
fn test_user_message_roundtrip() { fn test_user_message_roundtrip() {
crate::test_roundtrip_encode(UserMessage::Train(TrainMessage { data: 12 })); crate::test_roundtrip_write(UserMessage::Train(TrainMessage { data: 12 }));
crate::test_roundtrip_encode(UserMessage::SayText2(Box::new(SayText2Message { crate::test_roundtrip_write(UserMessage::SayText2(Box::new(SayText2Message {
client: 3, client: 3,
raw: 1, raw: 1,
kind: ChatMessageKind::ChatTeamDead, kind: ChatMessageKind::ChatTeamDead,
@ -297,7 +297,7 @@ impl BitWrite<LittleEndian> for SayText2Message {
#[test] #[test]
fn test_say_text2_roundtrip() { fn test_say_text2_roundtrip() {
crate::test_roundtrip_encode(SayText2Message { crate::test_roundtrip_write(SayText2Message {
client: 3, client: 3,
raw: 1, raw: 1,
kind: ChatMessageKind::ChatTeamDead, kind: ChatMessageKind::ChatTeamDead,

View file

@ -45,12 +45,12 @@ impl BitWrite<LittleEndian> for VoiceInitMessage {
#[test] #[test]
fn test_voice_init_roundtrip() { fn test_voice_init_roundtrip() {
crate::test_roundtrip_encode(VoiceInitMessage { crate::test_roundtrip_write(VoiceInitMessage {
codec: "foo".into(), codec: "foo".into(),
quality: 0, quality: 0,
sampling_rate: 0, sampling_rate: 0,
}); });
crate::test_roundtrip_encode(VoiceInitMessage { crate::test_roundtrip_write(VoiceInitMessage {
codec: "foo".into(), codec: "foo".into(),
quality: 255, quality: 255,
sampling_rate: 12, sampling_rate: 12,
@ -119,13 +119,13 @@ fn test_parse_sounds_roundtrip() {
use bitbuffer::BitReadBuffer; use bitbuffer::BitReadBuffer;
let inner = BitReadBuffer::new(&[1, 2, 3, 4, 5, 6], LittleEndian); let inner = BitReadBuffer::new(&[1, 2, 3, 4, 5, 6], LittleEndian);
crate::test_roundtrip_encode(ParseSoundsMessage { crate::test_roundtrip_write(ParseSoundsMessage {
reliable: false, reliable: false,
num: 0, num: 0,
length: inner.bit_len() as u16, length: inner.bit_len() as u16,
data: inner.clone().into(), data: inner.clone().into(),
}); });
crate::test_roundtrip_encode(ParseSoundsMessage { crate::test_roundtrip_write(ParseSoundsMessage {
reliable: true, reliable: true,
num: 1, num: 1,
length: inner.bit_len() as u16, length: inner.bit_len() as u16,

View file

@ -61,12 +61,12 @@ impl<E: Endianness> BitWrite<E> for ViewAngles {
#[test] #[test]
fn test_view_angles_roundtrip() { fn test_view_angles_roundtrip() {
crate::test_roundtrip_encode(ViewAngles { crate::test_roundtrip_write(ViewAngles {
origin: (Vector::default(), Vector::default()), origin: (Vector::default(), Vector::default()),
angles: (Vector::default(), Vector::default()), angles: (Vector::default(), Vector::default()),
local_angles: (Vector::default(), Vector::default()), local_angles: (Vector::default(), Vector::default()),
}); });
crate::test_roundtrip_encode(ViewAngles { crate::test_roundtrip_write(ViewAngles {
origin: ( origin: (
Vector { Vector {
x: 1.0, x: 1.0,

View file

@ -15,7 +15,7 @@ mod nullhasher;
#[cfg(test)] #[cfg(test)]
#[track_caller] #[track_caller]
fn test_roundtrip_encode< fn test_roundtrip_write<
'a, 'a,
T: bitbuffer::BitRead<'a, bitbuffer::LittleEndian> T: bitbuffer::BitRead<'a, bitbuffer::LittleEndian>
+ bitbuffer::BitWrite<bitbuffer::LittleEndian> + bitbuffer::BitWrite<bitbuffer::LittleEndian>
@ -41,6 +41,40 @@ fn test_roundtrip_encode<
assert_eq!( assert_eq!(
pos, pos,
read.pos(), read.pos(),
"Failed to assert that all encoded bytes are used for decoding" "Failed to assert that all encoded bits ({}) are used for decoding ({})",
pos,
read.pos()
);
}
#[cfg(test)]
#[track_caller]
fn test_roundtrip_encode<
'a,
T: Parse<'a> + crate::demo::parser::Encode + std::fmt::Debug + std::cmp::PartialEq,
>(
val: T,
state: &ParserState,
) {
let mut data = Vec::with_capacity(128);
use bitbuffer::{BitReadBuffer, BitReadStream, BitWriteStream, LittleEndian};
let pos = {
let mut stream = BitWriteStream::new(&mut data, LittleEndian);
val.encode(&mut stream, state).unwrap();
stream.bit_len()
};
let mut read = BitReadStream::new(BitReadBuffer::new_owned(data, LittleEndian));
assert_eq!(
val,
T::parse(&mut read, state).unwrap(),
"Failed to assert the parsed message is equal to the original"
);
assert_eq!(
pos,
read.pos(),
"Failed to assert that all encoded bits ({}) are used for decoding ({})",
pos,
read.pos()
); );
} }