1
0
Fork 0
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:
Robin Appelman 2019-12-06 18:58:30 +01:00
commit 5f6cfe077e
13 changed files with 379 additions and 9 deletions

View file

@ -4,10 +4,12 @@ use std::fs;
use main_error::MainError;
pub use tf_demo_parser::{Demo, DemoParser, Parse, ParseError, ParserState, Stream};
#[cfg(feature = "jemallocator")]
#[global_allocator]
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
fn main() -> Result<(), MainError> {
#[cfg(feature = "better_panic")]
better_panic::install();
let args: Vec<_> = env::args().collect();

View file

@ -9034,4 +9034,3 @@ pub fn get_sizes() -> std::collections::hash_map::HashMap<&'static str, usize> {
.into_iter()
.collect()
}

View file

@ -32,6 +32,12 @@ impl From<ClassId> for usize {
#[derive(BitRead, PartialEq, Eq, Hash, Debug, Serialize, Deserialize, Clone, Display)]
pub struct ServerClassName(Rc<String>);
impl ServerClassName {
pub fn as_str(&self) -> &str {
self.0.as_str()
}
}
impl From<String> for ServerClassName {
fn from(value: String) -> Self {
Self(Rc::new(value))
@ -50,6 +56,12 @@ pub struct ServerClass {
)]
pub struct SendTableName(Rc<String>);
impl SendTableName {
pub fn as_str(&self) -> &str {
self.0.as_str()
}
}
impl From<String> for SendTableName {
fn from(value: String) -> Self {
Self(Rc::new(value))

View 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()
}
_ => {}
}
}
_ => {}
}
}
}
}

View file

@ -15,7 +15,7 @@ pub trait MessageHandler {
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;
}
@ -114,7 +114,8 @@ impl<T: MessageHandler> DemoHandler<T> {
send_tables: Vec<ParseSendTable>,
server_classes: Vec<ServerClass>,
) {
self.analyser.handle_data_tables(&send_tables);
self.analyser
.handle_data_tables(&send_tables, &server_classes);
self.state_handler
.handle_data_table(send_tables, server_classes);
}

View file

@ -15,6 +15,7 @@ use err_derive::Error;
mod analyser;
mod error;
pub mod gamestateanalyser;
mod handler;
mod messagetypeanalyser;
mod state;

View file

@ -11,6 +11,7 @@ use crate::demo::message::stringtable::log_base2;
use crate::demo::packet::datatable::SendTableName;
use crate::demo::parser::MalformedSendPropDefinitionError;
use parse_display::Display;
use serde::export::TryFrom;
use std::convert::TryInto;
use std::fmt;
use std::rc::Rc;
@ -20,6 +21,12 @@ use std::rc::Rc;
)]
pub struct SendPropName(Rc<String>);
impl SendPropName {
pub fn as_str(&self) -> &str {
self.0.as_str()
}
}
impl PartialEq<&str> for SendPropName {
fn eq(&self, other: &&str) -> bool {
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)]
pub struct SendPropDefinitionIndex(u32);