use bitbuffer::{BitRead, LittleEndian};
#[cfg(feature = "write")]
use bitbuffer::{BitWrite, BitWriteSized, BitWriteStream};
use parse_display::Display;
use serde::{Deserialize, Serialize};
use std::borrow::Cow;
use crate::demo::gameevent_gen::GameEventType;
use crate::demo::gamevent::{
GameEvent, GameEventDefinition, GameEventEntry, GameEventValueType, RawGameEvent,
};
#[cfg(feature = "write")]
use crate::demo::parser::Encode;
use crate::demo::parser::ParseBitSkip;
use crate::{GameEventError, Parse, ParseError, ParserState, ReadResult, Result, Stream};
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
pub struct GameEventMessage {
pub event_type_id: GameEventTypeId,
pub event_type: GameEventType,
pub event: GameEvent,
}
impl Parse<'_> for GameEventMessage {
fn parse(stream: &mut Stream, state: &ParserState) -> Result<Self> {
let length: u16 = stream.read_sized(11)?;
let mut data = stream.read_bits(length as usize)?;
let event_type_id: GameEventTypeId = data.read()?;
if state.event_definitions.is_empty() {
return Ok(GameEventMessage {
event_type_id,
event_type: GameEventType::Unknown(String::new()),
event: GameEvent::Unknown(RawGameEvent {
event_type: GameEventType::Unknown(String::new()),
values: Vec::new(),
}),
});
}
let event = match state.event_definitions.get(usize::from(event_type_id)) {
Some(definition) => GameEvent::read(&mut data, definition)?,
None => {
return Err(ParseError::MalformedGameEvent(GameEventError::UnknownType(
event_type_id,
)));
}
};
Ok(GameEventMessage {
event_type_id,
event_type: event.event_type(),
event,
})
}
}
#[cfg(feature = "write")]
impl Encode for GameEventMessage {
fn encode(&self, stream: &mut BitWriteStream<LittleEndian>, state: &ParserState) -> Result<()> {
let definition = state
.event_definitions
.iter()
.find(|def| def.event_type == self.event_type)
.ok_or(ParseError::MalformedGameEvent(GameEventError::UnknownType(
self.event_type_id,
)))?;
stream.reserve_length(11, |stream| {
self.event_type_id.write(stream)?;
self.event.write(stream, definition)
})
}
}
#[cfg(feature = "write")]
#[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::new("reason", GameEventValueType::String)],
},
GameEventDefinition {
id: GameEventTypeId(1),
event_type: GameEventType::ServerChangeLevelFailed,
entries: vec![GameEventEntry::new(
"level_name",
GameEventValueType::String,
)],
},
GameEventDefinition {
id: GameEventTypeId(2),
event_type: GameEventType::GameInit,
entries: vec![],
},
];
let mut state = ParserState::new(24, |_| false, false);
state.event_definitions = definitions;
crate::test_roundtrip_encode(
GameEventMessage {
event_type_id: GameEventTypeId(0),
event_type: GameEventType::ServerShutdown,
event: GameEvent::ServerShutdown(ServerShutdownEvent {
reason: "asd".into(),
}),
},
&state,
);
crate::test_roundtrip_encode(
GameEventMessage {
event_type_id: GameEventTypeId(2),
event_type: GameEventType::GameInit,
event: GameEvent::GameInit(GameInitEvent {}),
},
&state,
);
}
impl ParseBitSkip<'_> for GameEventMessage {
fn parse_skip(stream: &mut Stream, _state: &ParserState) -> Result<()> {
let length: u16 = stream.read_sized(11)?;
stream.skip_bits(length as usize).map_err(ParseError::from)
}
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(
BitRead,
Debug,
Clone,
Copy,
PartialEq,
Eq,
Hash,
PartialOrd,
Ord,
Display,
Serialize,
Deserialize,
)]
#[cfg_attr(feature = "write", derive(BitWrite))]
pub struct GameEventTypeId(#[size = 9] u16);
impl From<GameEventTypeId> for usize {
fn from(id: GameEventTypeId) -> Self {
id.0 as usize
}
}
impl From<GameEventTypeId> for u16 {
fn from(id: GameEventTypeId) -> Self {
id.0
}
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
pub struct GameEventListMessage {
pub event_list: Vec<GameEventDefinition>,
}
impl BitRead<'_, LittleEndian> for GameEventDefinition {
fn read(stream: &mut Stream) -> ReadResult<Self> {
let event_type: GameEventTypeId = stream.read()?;
let name: String = stream.read()?;
let mut entries = Vec::new();
let mut entry_type = stream.read()?;
while entry_type != GameEventValueType::None {
let entry_name: Cow<str> = stream.read()?;
entries.push(GameEventEntry::new(entry_name, entry_type));
entry_type = stream.read()?;
}
Ok(GameEventDefinition {
id: event_type,
event_type: GameEventType::from_type_name(name.as_str()),
entries,
})
}
}
#[cfg(feature = "write")]
impl BitWrite<LittleEndian> for GameEventDefinition {
fn write(&self, stream: &mut BitWriteStream<LittleEndian>) -> ReadResult<()> {
self.id.write(stream)?;
self.event_type.as_str().write(stream)?;
for entry in self.entries.iter() {
entry.kind.write(stream)?;
entry.name.write(stream)?;
}
GameEventValueType::None.write(stream)?;
Ok(())
}
}
#[test]
#[cfg(feature = "write")]
fn test_event_definition_roundtrip() {
crate::test_roundtrip_write(GameEventDefinition {
id: GameEventTypeId(0),
event_type: GameEventType::ServerChangeLevelFailed,
entries: vec![GameEventEntry::new(
"level_name",
GameEventValueType::String,
)],
});
}
impl BitRead<'_, LittleEndian> for GameEventListMessage {
fn read(stream: &mut Stream) -> ReadResult<Self> {
let count: u16 = stream.read_sized(9)?;
let length: u32 = stream.read_sized(20)?;
let mut data = stream.read_bits(length as usize)?;
let event_list: Vec<GameEventDefinition> = data.read_sized(count as usize)?;
Ok(GameEventListMessage { event_list })
}
}
#[cfg(feature = "write")]
impl BitWrite<LittleEndian> for GameEventListMessage {
fn write(&self, stream: &mut BitWriteStream<LittleEndian>) -> ReadResult<()> {
(self.event_list.len() as u16).write_sized(stream, 9)?;
stream.reserve_length(20, |stream| {
for event in self.event_list.iter() {
event.write(stream)?;
}
Ok(())
})
}
}
#[test]
#[cfg(feature = "write")]
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::new(
"level_name",
GameEventValueType::String,
)],
}],
});
crate::test_roundtrip_write(GameEventListMessage {
event_list: vec![
GameEventDefinition {
id: GameEventTypeId(0),
event_type: GameEventType::ServerSpawn,
entries: vec![
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::new(
"level_name",
GameEventValueType::String,
)],
},
GameEventDefinition {
id: GameEventTypeId(2),
event_type: GameEventType::GameInit,
entries: vec![],
},
],
});
}