mirror of
https://codeberg.org/demostf/parser.git
synced 2026-06-03 18:24:05 +02:00
gamestate analyser wip
This commit is contained in:
parent
6222258ac2
commit
5f6cfe077e
13 changed files with 379 additions and 9 deletions
|
|
@ -25,11 +25,11 @@ serde_repr = "0.1"
|
||||||
err-derive = "0.2"
|
err-derive = "0.2"
|
||||||
parse-display = "0.1"
|
parse-display = "0.1"
|
||||||
main_error = "0.1.0"
|
main_error = "0.1.0"
|
||||||
|
jemallocator = { version = "0.3", optional = true }
|
||||||
|
better-panic = { version = "0.1", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pretty_assertions = "0.6"
|
pretty_assertions = "0.6"
|
||||||
jemallocator = "0.3"
|
|
||||||
better-panic = "0.1"
|
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = true
|
lto = true
|
||||||
1
data/gully_game_state.json
Normal file
1
data/gully_game_state.json
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{"players":[{"entity":2,"position":{"x":5209.61,"y":-1013.047,"z":296.03125},"health":125,"max_health":125,"class":1,"team":"blue","view_angle":64.04692,"state":"Alive"},{"entity":3,"position":{"x":4563.5,"y":-1381.375,"z":369.125},"health":135,"max_health":200,"class":3,"team":"red","view_angle":28.504398,"state":"Alive"},{"entity":4,"position":{"x":4334.875,"y":-1428.875,"z":296.0},"health":104,"max_health":150,"class":5,"team":"red","view_angle":355.0733,"state":"Death"},{"entity":5,"position":{"x":5207.0,"y":-1077.0,"z":296.0},"health":175,"max_health":175,"class":4,"team":"blue","view_angle":33.079178,"state":"Alive"},{"entity":6,"position":{"x":5046.0,"y":-1077.0,"z":296.0},"health":125,"max_health":125,"class":1,"team":"blue","view_angle":180.17595,"state":"Alive"},{"entity":7,"position":{"x":5046.0,"y":-1013.0,"z":296.0},"health":150,"max_health":150,"class":5,"team":"blue","view_angle":180.17595,"state":"Alive"},{"entity":8,"position":{"x":4124.375,"y":-990.375,"z":162.0},"health":260,"max_health":175,"class":4,"team":"red","view_angle":355.42523,"state":"Alive"},{"entity":10,"position":{"x":5046.0,"y":-953.25,"z":296.0},"health":200,"max_health":200,"class":3,"team":"blue","view_angle":180.17595,"state":"Alive"},{"entity":12,"position":{"x":3954.25,"y":-1329.875,"z":232.0},"health":132,"max_health":125,"class":2,"team":"red","view_angle":17.947214,"state":"Alive"},{"entity":13,"position":{"x":4563.5,"y":-1381.375,"z":369.125},"health":1,"max_health":200,"class":3,"team":"red","view_angle":9.853373,"state":"Death"},{"entity":11,"position":{"x":5046.0,"y":-1141.0,"z":296.0},"health":175,"max_health":175,"class":7,"team":"Other","view_angle":180.17595,"state":"Alive"},{"entity":9,"position":{"x":3707.125,"y":-444.375,"z":342.5},"health":125,"max_health":125,"class":1,"team":"Other","view_angle":110.14663,"state":"Alive"}],"buildings":[]}
|
||||||
1
data/small_game_state.json
Normal file
1
data/small_game_state.json
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{"players":[{"entity":1,"position":{"x":-3599.1775,"y":421.331,"z":298.0},"health":125,"max_health":125,"class":1,"team":"red","view_angle":0.0,"state":"Alive"}],"buildings":[]}
|
||||||
|
|
@ -4,10 +4,12 @@ use std::fs;
|
||||||
use main_error::MainError;
|
use main_error::MainError;
|
||||||
pub use tf_demo_parser::{Demo, DemoParser, Parse, ParseError, ParserState, Stream};
|
pub use tf_demo_parser::{Demo, DemoParser, Parse, ParseError, ParserState, Stream};
|
||||||
|
|
||||||
|
#[cfg(feature = "jemallocator")]
|
||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
|
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
|
||||||
|
|
||||||
fn main() -> Result<(), MainError> {
|
fn main() -> Result<(), MainError> {
|
||||||
|
#[cfg(feature = "better_panic")]
|
||||||
better_panic::install();
|
better_panic::install();
|
||||||
|
|
||||||
let args: Vec<_> = env::args().collect();
|
let args: Vec<_> = env::args().collect();
|
||||||
|
|
|
||||||
|
|
@ -9034,4 +9034,3 @@ pub fn get_sizes() -> std::collections::hash_map::HashMap<&'static str, usize> {
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,12 @@ impl From<ClassId> for usize {
|
||||||
#[derive(BitRead, PartialEq, Eq, Hash, Debug, Serialize, Deserialize, Clone, Display)]
|
#[derive(BitRead, PartialEq, Eq, Hash, Debug, Serialize, Deserialize, Clone, Display)]
|
||||||
pub struct ServerClassName(Rc<String>);
|
pub struct ServerClassName(Rc<String>);
|
||||||
|
|
||||||
|
impl ServerClassName {
|
||||||
|
pub fn as_str(&self) -> &str {
|
||||||
|
self.0.as_str()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<String> for ServerClassName {
|
impl From<String> for ServerClassName {
|
||||||
fn from(value: String) -> Self {
|
fn from(value: String) -> Self {
|
||||||
Self(Rc::new(value))
|
Self(Rc::new(value))
|
||||||
|
|
@ -50,6 +56,12 @@ pub struct ServerClass {
|
||||||
)]
|
)]
|
||||||
pub struct SendTableName(Rc<String>);
|
pub struct SendTableName(Rc<String>);
|
||||||
|
|
||||||
|
impl SendTableName {
|
||||||
|
pub fn as_str(&self) -> &str {
|
||||||
|
self.0.as_str()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<String> for SendTableName {
|
impl From<String> for SendTableName {
|
||||||
fn from(value: String) -> Self {
|
fn from(value: String) -> Self {
|
||||||
Self(Rc::new(value))
|
Self(Rc::new(value))
|
||||||
|
|
|
||||||
260
src/demo/parser/gamestateanalyser.rs
Normal file
260
src/demo/parser/gamestateanalyser.rs
Normal file
|
|
@ -0,0 +1,260 @@
|
||||||
|
use crate::demo::gameevent_gen::GameEventType::PlayerSappedObject;
|
||||||
|
use crate::demo::message::packetentities::{EntityId, PacketEntity};
|
||||||
|
use crate::demo::message::Message;
|
||||||
|
use crate::demo::packet::datatable::{ParseSendTable, SendTableName, ServerClass, ServerClassName};
|
||||||
|
use crate::demo::parser::analyser::{Class, Team, UserId};
|
||||||
|
use crate::demo::parser::MessageHandler;
|
||||||
|
use crate::demo::vector::{Vector, VectorXY};
|
||||||
|
use crate::{MessageType, ParserState};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
pub struct CachedEntities {}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
|
||||||
|
pub enum PlayerState {
|
||||||
|
Alive = 0,
|
||||||
|
Dying = 1,
|
||||||
|
Death = 2,
|
||||||
|
Respawnable = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlayerState {
|
||||||
|
pub fn new(number: i64) -> Self {
|
||||||
|
match number {
|
||||||
|
1 => PlayerState::Dying,
|
||||||
|
2 => PlayerState::Death,
|
||||||
|
3 => PlayerState::Respawnable,
|
||||||
|
_ => PlayerState::Alive,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
|
pub struct Player {
|
||||||
|
entity: EntityId,
|
||||||
|
position: Vector,
|
||||||
|
health: u16,
|
||||||
|
max_health: u16,
|
||||||
|
class: Class,
|
||||||
|
team: Team,
|
||||||
|
view_angle: f32,
|
||||||
|
state: PlayerState,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
|
pub enum Building {
|
||||||
|
Sentry {
|
||||||
|
builder: UserId,
|
||||||
|
position: Vector,
|
||||||
|
level: u8,
|
||||||
|
max_health: u16,
|
||||||
|
health: u16,
|
||||||
|
building: bool,
|
||||||
|
sapped: bool,
|
||||||
|
team: Team,
|
||||||
|
angle: f32,
|
||||||
|
player_controller: bool,
|
||||||
|
auto_aim_target: UserId,
|
||||||
|
shells: u16,
|
||||||
|
rockets: u16,
|
||||||
|
is_mini: bool,
|
||||||
|
},
|
||||||
|
Dispenser {
|
||||||
|
builder: UserId,
|
||||||
|
position: Vector,
|
||||||
|
level: u8,
|
||||||
|
max_health: u16,
|
||||||
|
health: u16,
|
||||||
|
building: bool,
|
||||||
|
sapped: bool,
|
||||||
|
team: Team,
|
||||||
|
angle: f32,
|
||||||
|
healing: Vec<UserId>,
|
||||||
|
metal: u16,
|
||||||
|
},
|
||||||
|
Teleporter {
|
||||||
|
builder: UserId,
|
||||||
|
position: Vector,
|
||||||
|
level: u8,
|
||||||
|
max_health: u16,
|
||||||
|
health: u16,
|
||||||
|
building: bool,
|
||||||
|
sapped: bool,
|
||||||
|
team: Team,
|
||||||
|
angle: f32,
|
||||||
|
is_entrance: bool,
|
||||||
|
other_end: EntityId,
|
||||||
|
recharge_time: f32,
|
||||||
|
recharge_duration: f32,
|
||||||
|
times_used: u16,
|
||||||
|
yaw_to_exit: f32,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Serialize, Deserialize, PartialEq)]
|
||||||
|
pub struct GameState {
|
||||||
|
players: Vec<Player>,
|
||||||
|
buildings: Vec<Building>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GameState {
|
||||||
|
pub fn get_or_create_player(&mut self, entity_id: EntityId) -> &mut Player {
|
||||||
|
let index = match self
|
||||||
|
.players
|
||||||
|
.iter_mut()
|
||||||
|
.enumerate()
|
||||||
|
.find(|(index, player)| player.entity == entity_id)
|
||||||
|
.map(|(index, _)| index)
|
||||||
|
{
|
||||||
|
Some(index) => index,
|
||||||
|
None => {
|
||||||
|
let player = Player {
|
||||||
|
entity: entity_id,
|
||||||
|
position: Vector::default(),
|
||||||
|
health: 0,
|
||||||
|
max_health: 0,
|
||||||
|
class: Class::Other,
|
||||||
|
team: Team::Other,
|
||||||
|
view_angle: 0.0,
|
||||||
|
state: PlayerState::Alive,
|
||||||
|
};
|
||||||
|
|
||||||
|
let index = self.players.len();
|
||||||
|
self.players.push(player);
|
||||||
|
index
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
&mut self.players[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug)]
|
||||||
|
pub struct GameStateAnalyser {
|
||||||
|
pub state: GameState,
|
||||||
|
class_names: Vec<ServerClassName>, // indexed by ClassId
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MessageHandler for GameStateAnalyser {
|
||||||
|
type Output = GameState;
|
||||||
|
|
||||||
|
fn does_handle(message_type: MessageType) -> bool {
|
||||||
|
match message_type {
|
||||||
|
MessageType::PacketEntities => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_message(&mut self, message: &Message, tick: u32) {
|
||||||
|
match message {
|
||||||
|
Message::PacketEntities(message) => {
|
||||||
|
for entity in &message.entities {
|
||||||
|
self.handle_entity(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_data_tables(&mut self, tables: &[ParseSendTable], server_classes: &[ServerClass]) {
|
||||||
|
self.class_names = server_classes
|
||||||
|
.iter()
|
||||||
|
.map(|class| &class.name)
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_output(self, state: ParserState) -> Self::Output {
|
||||||
|
self.state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GameStateAnalyser {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_entity(&mut self, entity: &PacketEntity) {
|
||||||
|
let class_name: &str = self
|
||||||
|
.class_names
|
||||||
|
.get(usize::from(entity.server_class))
|
||||||
|
.map(|class_name| class_name.as_str())
|
||||||
|
.unwrap_or("");
|
||||||
|
match class_name {
|
||||||
|
"CTFPlayer" => self.handle_player_entity(entity),
|
||||||
|
"CTFPlayerResource" => self.handle_player_resource(entity),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_player_resource(&mut self, entity: &PacketEntity) {
|
||||||
|
for prop in &entity.props {
|
||||||
|
if let Ok(player_id) = u32::from_str(prop.definition.name.as_str()) {
|
||||||
|
let entity_id = EntityId::from(player_id);
|
||||||
|
if let Some(player) = self
|
||||||
|
.state
|
||||||
|
.players
|
||||||
|
.iter_mut()
|
||||||
|
.find(|player| player.entity == entity_id)
|
||||||
|
{
|
||||||
|
match prop.definition.owner_table.as_str() {
|
||||||
|
"m_iTeam" => {
|
||||||
|
player.team =
|
||||||
|
Team::new(i64::try_from(&prop.value).unwrap_or_default() as u16)
|
||||||
|
}
|
||||||
|
"m_iMaxHealth" => {
|
||||||
|
player.max_health =
|
||||||
|
i64::try_from(&prop.value).unwrap_or_default() as u16
|
||||||
|
}
|
||||||
|
"m_iPlayerClass" => {
|
||||||
|
player.class =
|
||||||
|
Class::new(i64::try_from(&prop.value).unwrap_or_default() as u16)
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_player_entity(&mut self, entity: &PacketEntity) {
|
||||||
|
let player = self.state.get_or_create_player(entity.entity_index);
|
||||||
|
|
||||||
|
for prop in &entity.props {
|
||||||
|
match prop.definition.owner_table.as_str() {
|
||||||
|
"DT_BasePlayer" => match prop.definition.name.as_str() {
|
||||||
|
"m_iHealth" => {
|
||||||
|
player.health = i64::try_from(&prop.value).unwrap_or_default() as u16
|
||||||
|
}
|
||||||
|
"m_iMaxHealth" => {
|
||||||
|
player.max_health = i64::try_from(&prop.value).unwrap_or_default() as u16
|
||||||
|
}
|
||||||
|
"m_lifeState" => {
|
||||||
|
player.state =
|
||||||
|
PlayerState::new(i64::try_from(&prop.value).unwrap_or_default())
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
"DT_TFLocalPlayerExclusive" | "DT_TFNonLocalPlayerExclusive" => {
|
||||||
|
match prop.definition.name.as_str() {
|
||||||
|
"m_vecOrigin" => {
|
||||||
|
let pos_xy = VectorXY::try_from(&prop.value).unwrap_or_default();
|
||||||
|
player.position.x = pos_xy.x;
|
||||||
|
player.position.y = pos_xy.y;
|
||||||
|
}
|
||||||
|
"m_vecOrigin[2]" => {
|
||||||
|
player.position.z = f32::try_from(&prop.value).unwrap_or_default()
|
||||||
|
}
|
||||||
|
"m_angEyeAngles[1]" => {
|
||||||
|
player.view_angle = f32::try_from(&prop.value).unwrap_or_default()
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -15,7 +15,7 @@ pub trait MessageHandler {
|
||||||
|
|
||||||
fn handle_string_entry(&mut self, table: &String, index: usize, entries: &StringTableEntry) {}
|
fn handle_string_entry(&mut self, table: &String, index: usize, entries: &StringTableEntry) {}
|
||||||
|
|
||||||
fn handle_data_tables(&mut self, tables: &[ParseSendTable]) {}
|
fn handle_data_tables(&mut self, tables: &[ParseSendTable], server_classes: &[ServerClass]) {}
|
||||||
|
|
||||||
fn get_output(self, state: ParserState) -> Self::Output;
|
fn get_output(self, state: ParserState) -> Self::Output;
|
||||||
}
|
}
|
||||||
|
|
@ -114,7 +114,8 @@ impl<T: MessageHandler> DemoHandler<T> {
|
||||||
send_tables: Vec<ParseSendTable>,
|
send_tables: Vec<ParseSendTable>,
|
||||||
server_classes: Vec<ServerClass>,
|
server_classes: Vec<ServerClass>,
|
||||||
) {
|
) {
|
||||||
self.analyser.handle_data_tables(&send_tables);
|
self.analyser
|
||||||
|
.handle_data_tables(&send_tables, &server_classes);
|
||||||
self.state_handler
|
self.state_handler
|
||||||
.handle_data_table(send_tables, server_classes);
|
.handle_data_table(send_tables, server_classes);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ use err_derive::Error;
|
||||||
|
|
||||||
mod analyser;
|
mod analyser;
|
||||||
mod error;
|
mod error;
|
||||||
|
pub mod gamestateanalyser;
|
||||||
mod handler;
|
mod handler;
|
||||||
mod messagetypeanalyser;
|
mod messagetypeanalyser;
|
||||||
mod state;
|
mod state;
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ use crate::demo::message::stringtable::log_base2;
|
||||||
use crate::demo::packet::datatable::SendTableName;
|
use crate::demo::packet::datatable::SendTableName;
|
||||||
use crate::demo::parser::MalformedSendPropDefinitionError;
|
use crate::demo::parser::MalformedSendPropDefinitionError;
|
||||||
use parse_display::Display;
|
use parse_display::Display;
|
||||||
|
use serde::export::TryFrom;
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
@ -20,6 +21,12 @@ use std::rc::Rc;
|
||||||
)]
|
)]
|
||||||
pub struct SendPropName(Rc<String>);
|
pub struct SendPropName(Rc<String>);
|
||||||
|
|
||||||
|
impl SendPropName {
|
||||||
|
pub fn as_str(&self) -> &str {
|
||||||
|
self.0.as_str()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PartialEq<&str> for SendPropName {
|
impl PartialEq<&str> for SendPropName {
|
||||||
fn eq(&self, other: &&str) -> bool {
|
fn eq(&self, other: &&str) -> bool {
|
||||||
self.0.as_str() == *other
|
self.0.as_str() == *other
|
||||||
|
|
@ -528,6 +535,66 @@ impl From<Vec<SendPropValue>> for SendPropValue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&SendPropValue> for i64 {
|
||||||
|
type Error = ();
|
||||||
|
fn try_from(value: &SendPropValue) -> std::result::Result<Self, Self::Error> {
|
||||||
|
match value {
|
||||||
|
SendPropValue::Integer(val) => Ok(*val),
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&SendPropValue> for Vector {
|
||||||
|
type Error = ();
|
||||||
|
fn try_from(value: &SendPropValue) -> std::result::Result<Self, Self::Error> {
|
||||||
|
match value {
|
||||||
|
SendPropValue::Vector(val) => Ok(*val),
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&SendPropValue> for VectorXY {
|
||||||
|
type Error = ();
|
||||||
|
fn try_from(value: &SendPropValue) -> std::result::Result<Self, Self::Error> {
|
||||||
|
match value {
|
||||||
|
SendPropValue::VectorXY(val) => Ok(*val),
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&SendPropValue> for f32 {
|
||||||
|
type Error = ();
|
||||||
|
fn try_from(value: &SendPropValue) -> std::result::Result<Self, Self::Error> {
|
||||||
|
match value {
|
||||||
|
SendPropValue::Float(val) => Ok(*val),
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TryFrom<&'a SendPropValue> for &'a str {
|
||||||
|
type Error = ();
|
||||||
|
fn try_from(value: &'a SendPropValue) -> std::result::Result<Self, Self::Error> {
|
||||||
|
match value {
|
||||||
|
SendPropValue::String(val) => Ok(val.as_str()),
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TryFrom<&'a SendPropValue> for &'a [SendPropValue] {
|
||||||
|
type Error = ();
|
||||||
|
fn try_from(value: &'a SendPropValue) -> std::result::Result<Self, Self::Error> {
|
||||||
|
match value {
|
||||||
|
SendPropValue::Array(val) => Ok(val.as_slice()),
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
|
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
|
||||||
pub struct SendPropDefinitionIndex(u32);
|
pub struct SendPropDefinitionIndex(u32);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -195,7 +195,5 @@ fn entity_test(input_file: &str, snapshot_file: &str) {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn entity_test_short() {
|
fn entity_test_short() {
|
||||||
better_panic::install();
|
|
||||||
|
|
||||||
entity_test("data/small.dem", "data/small_entities.json");
|
entity_test("data/small.dem", "data/small_entities.json");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,9 @@ use std::fs;
|
||||||
|
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use tf_demo_parser::demo::message::Message;
|
use tf_demo_parser::demo::message::Message;
|
||||||
use tf_demo_parser::demo::packet::datatable::{ParseSendTable, SendTable, SendTableName};
|
use tf_demo_parser::demo::packet::datatable::{
|
||||||
|
ParseSendTable, SendTable, SendTableName, ServerClass,
|
||||||
|
};
|
||||||
use tf_demo_parser::demo::packet::stringtable::StringTableEntry;
|
use tf_demo_parser::demo::packet::stringtable::StringTableEntry;
|
||||||
use tf_demo_parser::demo::parser::MessageHandler;
|
use tf_demo_parser::demo::parser::MessageHandler;
|
||||||
use tf_demo_parser::demo::sendprop::SendPropDefinition;
|
use tf_demo_parser::demo::sendprop::SendPropDefinition;
|
||||||
|
|
@ -30,7 +32,7 @@ impl MessageHandler for SendPropAnalyser {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_data_tables(&mut self, tables: &[ParseSendTable]) {
|
fn handle_data_tables(&mut self, tables: &[ParseSendTable], server_classes: &[ServerClass]) {
|
||||||
self.tables = tables.to_vec()
|
self.tables = tables.to_vec()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
||||||
|
use tf_demo_parser::demo::parser::gamestateanalyser::{GameState, GameStateAnalyser};
|
||||||
use tf_demo_parser::{Demo, DemoParser, MatchState, MessageType, MessageTypeAnalyser};
|
use tf_demo_parser::{Demo, DemoParser, MatchState, MessageType, MessageTypeAnalyser};
|
||||||
|
|
||||||
fn snapshot_test(input_file: &str, snapshot_file: &str) {
|
fn snapshot_test(input_file: &str, snapshot_file: &str) {
|
||||||
|
|
@ -32,6 +33,21 @@ fn test_message_types(input_file: &str, snapshot_file: &str) {
|
||||||
assert_eq!(expected, message_types);
|
assert_eq!(expected, message_types);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn game_state_test(input_file: &str, snapshot_file: &str) {
|
||||||
|
let file = fs::read(input_file).expect("Unable to read file");
|
||||||
|
let demo = Demo::new(file);
|
||||||
|
let (_, state) =
|
||||||
|
DemoParser::parse_with_analyser(demo.get_stream(), GameStateAnalyser::new()).unwrap();
|
||||||
|
|
||||||
|
let expected: GameState = serde_json::from_slice(
|
||||||
|
fs::read(snapshot_file)
|
||||||
|
.expect("Unable to read file")
|
||||||
|
.as_slice(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(expected, state);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn snapshot_test_small() {
|
fn snapshot_test_small() {
|
||||||
snapshot_test("data/small.dem", "data/small.json");
|
snapshot_test("data/small.dem", "data/small.json");
|
||||||
|
|
@ -56,3 +72,13 @@ fn snapshot_test_malformed_cvar() {
|
||||||
fn snapshot_test_decal() {
|
fn snapshot_test_decal() {
|
||||||
snapshot_test("data/decal.dem", "data/decal.json");
|
snapshot_test("data/decal.dem", "data/decal.json");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn game_state_test_small() {
|
||||||
|
game_state_test("data/small.dem", "data/small_game_state.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn game_state_test_gully() {
|
||||||
|
game_state_test("data/gully.dem", "data/gully_game_state.json");
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue