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

make gameevent parsing more backwards compatible

This commit is contained in:
Robin Appelman 2024-04-16 20:45:19 +02:00
commit ab87b34de8
6 changed files with 16415 additions and 4409 deletions

View file

@ -19,5 +19,8 @@ in
cargoLock = { cargoLock = {
lockFile = ./Cargo.lock; lockFile = ./Cargo.lock;
outputHashes = {
"schemars-0.8.16" = "sha256-mQR56Ym76gSRulZrThmZHHw2JfhEgYhWXabwaYmyMYs=";
};
}; };
} }

View file

@ -225,7 +225,9 @@ pub fn generate_game_events(demo: Demo) -> TokenStream {
let span = Span::call_site(); let span = Span::call_site();
let imports = quote!( let imports = quote!(
use super::gamevent::{EventValue, GameEventDefinition, GameEventEntry, RawGameEvent}; use super::gamevent::{
EventValue, GameEventDefinition, GameEventEntry, RawGameEvent, GameEventValue,
};
use crate::demo::Stream; use crate::demo::Stream;
use crate::{ParseError, Result}; use crate::{ParseError, Result};
use bitbuffer::{BitRead, LittleEndian, BitWrite, BitWriteStream}; use bitbuffer::{BitRead, LittleEndian, BitWrite, BitWriteStream};
@ -241,32 +243,31 @@ pub fn generate_game_events(demo: Demo) -> TokenStream {
quote!(pub #name: #ty,) quote!(pub #name: #ty,)
}); });
let name = Ident::new( let field_getters = event.entries.iter().map(|entry| {
&format!("{}Event", get_event_name(event.event_type.as_str())), let raw_name = &entry.name;
span, let name = get_entry_name(&entry.name);
); let ident = Ident::new(name.as_str(), span);
quote!(#raw_name => Ok(self.#ident.clone().into()))
});
let event_name = get_event_name(event.event_type.as_str());
let name = Ident::new(&format!("{event_name}Event"), span);
let entry_readers = event.entries.iter().map(|entry| { let entry_readers = event.entries.iter().map(|entry| {
let raw_name = &entry.name;
let name_str = get_entry_name(&entry.name); let name_str = get_entry_name(&entry.name);
let name = Ident::new(&name_str, span); let name = Ident::new(&name_str, span);
let ty = Ident::new(get_type_name(entry.kind), span); let ty = Ident::new(get_type_name(entry.kind), span);
quote!( quote!(
#name: read_value::<#ty>(stream, iter.next(), #name_str)?, #name: read_value::<#ty>(stream, definition.get_entry(#raw_name), #name_str)?,
) )
}); });
let definition_iter = if event.entries.len() > 0 {
quote!(
let mut iter = definition.entries.iter();
)
} else {
quote!()
};
quote!( quote!(
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, BitWrite, PartialEq, Serialize, Deserialize, Clone)] #[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
pub struct #name { pub struct #name {
#(#fields)* #(#fields)*
} }
@ -274,12 +275,30 @@ pub fn generate_game_events(demo: Demo) -> TokenStream {
impl #name { impl #name {
#[allow(unused_variables)] #[allow(unused_variables)]
fn read(stream: &mut Stream, definition: &GameEventDefinition) -> Result<Self> { fn read(stream: &mut Stream, definition: &GameEventDefinition) -> Result<Self> {
#definition_iter
Ok(#name { Ok(#name {
#(#entry_readers)* #(#entry_readers)*
}) })
} }
#[allow(unused_variables)]
fn get_field(&self, field: &str) -> Result<GameEventValue> {
match field {
#(#field_getters,)*
_ => Err(ParseError::MissingGameEventValue {
ty: #event_name,
field: field.into(),
}),
}
}
#[allow(unused_variables)]
fn write(&self, stream: &mut BitWriteStream<LittleEndian>, definition: &GameEventDefinition) -> Result<()> {
for entry in &definition.entries {
let value = self.get_field(&entry.name).unwrap_or_else(|_| entry.kind.default_value());
stream.write(&value)?;
}
Ok(())
}
} }
) )
}); });
@ -353,7 +372,7 @@ pub fn generate_game_events(demo: Demo) -> TokenStream {
let variant_name = Ident::new(&name, span); let variant_name = Ident::new(&name, span);
quote!( quote!(
GameEvent::#variant_name(event) => event.write(stream), GameEvent::#variant_name(event) => event.write(stream, definition),
) )
}); });
@ -430,10 +449,10 @@ pub fn generate_game_events(demo: Demo) -> TokenStream {
GameEventType::Unknown(_) => GameEvent::Unknown(RawGameEvent::read(stream, definition)?), GameEventType::Unknown(_) => GameEvent::Unknown(RawGameEvent::read(stream, definition)?),
}) })
} }
pub fn write(&self, stream: &mut BitWriteStream<LittleEndian>) -> bitbuffer::Result<()> { pub fn write(&self, stream: &mut BitWriteStream<LittleEndian>, definition: &GameEventDefinition) -> Result<()> {
match &self { match &self {
#(#write_events)* #(#write_events)*
GameEvent::Unknown(raw) => raw.write(stream), GameEvent::Unknown(raw) => Ok(raw.write(stream)?),
} }
} }
pub fn event_type(&self) -> GameEventType { pub fn event_type(&self) -> GameEventType {

File diff suppressed because it is too large Load diff

View file

@ -15,6 +15,12 @@ pub struct GameEventDefinition {
pub entries: Vec<GameEventEntry>, pub entries: Vec<GameEventEntry>,
} }
impl GameEventDefinition {
pub fn get_entry(&self, name: &str) -> Option<&GameEventEntry> {
self.entries.iter().find(|entry| entry.name == name)
}
}
impl PartialEq<GameEventDefinition> for GameEventDefinition { impl PartialEq<GameEventDefinition> for GameEventDefinition {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.id.eq(&other.id) self.id.eq(&other.id)
@ -56,6 +62,21 @@ pub enum GameEventValueType {
Local = 7, Local = 7,
} }
impl GameEventValueType {
pub fn default_value(&self) -> GameEventValue {
match self {
GameEventValueType::None => GameEventValue::Local,
GameEventValueType::String => GameEventValue::String(Default::default()),
GameEventValueType::Float => GameEventValue::Float(Default::default()),
GameEventValueType::Long => GameEventValue::Long(Default::default()),
GameEventValueType::Short => GameEventValue::Short(Default::default()),
GameEventValueType::Byte => GameEventValue::Byte(Default::default()),
GameEventValueType::Boolean => GameEventValue::Boolean(Default::default()),
GameEventValueType::Local => GameEventValue::Local,
}
}
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum GameEventValue { pub enum GameEventValue {
@ -68,6 +89,42 @@ pub enum GameEventValue {
Local, Local,
} }
impl From<MaybeUtf8String> for GameEventValue {
fn from(value: MaybeUtf8String) -> Self {
GameEventValue::String(value)
}
}
impl From<f32> for GameEventValue {
fn from(value: f32) -> Self {
GameEventValue::Float(value)
}
}
impl From<u32> for GameEventValue {
fn from(value: u32) -> Self {
GameEventValue::Long(value)
}
}
impl From<u16> for GameEventValue {
fn from(value: u16) -> Self {
GameEventValue::Short(value)
}
}
impl From<u8> for GameEventValue {
fn from(value: u8) -> Self {
GameEventValue::Byte(value)
}
}
impl From<bool> for GameEventValue {
fn from(value: bool) -> Self {
GameEventValue::Boolean(value)
}
}
fn read_event_value(stream: &mut Stream, definition: &GameEventEntry) -> Result<GameEventValue> { fn read_event_value(stream: &mut Stream, definition: &GameEventEntry) -> Result<GameEventValue> {
Ok(match definition.kind { Ok(match definition.kind {
GameEventValueType::String => GameEventValue::String(stream.read()?), GameEventValueType::String => GameEventValue::String(stream.read()?),

View file

@ -52,14 +52,17 @@ impl Parse<'_> for GameEventMessage {
} }
impl Encode for GameEventMessage { impl Encode for GameEventMessage {
fn encode( fn encode(&self, stream: &mut BitWriteStream<LittleEndian>, state: &ParserState) -> Result<()> {
&self, let definition = state
stream: &mut BitWriteStream<LittleEndian>, .event_definitions
_state: &ParserState, .iter()
) -> Result<()> { .find(|def| def.event_type == self.event_type)
.ok_or_else(|| {
ParseError::MalformedGameEvent(GameEventError::UnknownType(self.event_type_id))
})?;
Ok(stream.reserve_length(11, |stream| { Ok(stream.reserve_length(11, |stream| {
self.event_type_id.write(stream)?; self.event_type_id.write(stream)?;
self.event.write(stream) self.event.write(stream, definition)
})?) })?)
} }
} }

View file

@ -63,6 +63,12 @@ pub enum ParseError {
name: &'static str, name: &'static str,
found_type: GameEventValueType, found_type: GameEventValueType,
}, },
#[error(
display = "Game event of type {} does not contain a {} value",
ty,
field
)]
MissingGameEventValue { ty: &'static str, field: String },
#[error(display = "An entity with an unknown server class({}) was read", _0)] #[error(display = "An entity with an unknown server class({}) was read", _0)]
UnknownServerClass(ClassId), UnknownServerClass(ClassId),
#[error(display = "Unknown send table: {}", _0)] #[error(display = "Unknown send table: {}", _0)]