mirror of
https://codeberg.org/demostf/tf-demos-viewer.git
synced 2026-06-03 18:14:11 +02:00
events
This commit is contained in:
parent
0dd6692af7
commit
72027c010c
4 changed files with 96 additions and 6 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
|
@ -561,9 +561,11 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tf-demos-viewer"
|
name = "tf-demos-viewer"
|
||||||
version = "0.2.0"
|
version = "0.2.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js-sys",
|
"js-sys",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"tf-demo-parser",
|
"tf-demo-parser",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
[package]
|
[package]
|
||||||
name = "tf-demos-viewer"
|
name = "tf-demos-viewer"
|
||||||
description = "JS bindings for demo parser"
|
description = "JS bindings for demo parser"
|
||||||
version = "0.2.0"
|
version = "0.2.1"
|
||||||
authors = ["Robin Appelman <robin@icewind.nl>"]
|
authors = ["Robin Appelman <robin@icewind.nl>"]
|
||||||
categories = ["wasm"]
|
categories = ["wasm"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
@ -27,4 +27,6 @@ wee_alloc = { version = "0.4.2", optional = true }
|
||||||
web-sys = { version = "0.3.22", features = ["console"] }
|
web-sys = { version = "0.3.22", features = ["console"] }
|
||||||
js-sys = "0.3.22"
|
js-sys = "0.3.22"
|
||||||
tf-demo-parser = { version = "0.5.1", path = "../tf-demo-parser" }
|
tf-demo-parser = { version = "0.5.1", path = "../tf-demo-parser" }
|
||||||
|
serde = { version = "1.0.215", features = ["derive"] }
|
||||||
|
serde_json = "1.0.133"
|
||||||
|
|
||||||
|
|
|
||||||
16
src/lib.rs
16
src/lib.rs
|
|
@ -1,6 +1,6 @@
|
||||||
#![macro_use]
|
#![macro_use]
|
||||||
|
|
||||||
use crate::state::ParsedDemo;
|
use crate::state::{ParsedDemo, SearchableEvent};
|
||||||
use js_sys::Function;
|
use js_sys::Function;
|
||||||
use tf_demo_parser::demo::header::Header;
|
use tf_demo_parser::demo::header::Header;
|
||||||
use tf_demo_parser::demo::parser::analyser::UserInfo;
|
use tf_demo_parser::demo::parser::analyser::UserInfo;
|
||||||
|
|
@ -54,6 +54,7 @@ pub struct FlatState {
|
||||||
victims: Box<[u8]>,
|
victims: Box<[u8]>,
|
||||||
weapons: Vec<String>,
|
weapons: Vec<String>,
|
||||||
player_info: Vec<UserInfo>,
|
player_info: Vec<UserInfo>,
|
||||||
|
events: Vec<SearchableEvent>,
|
||||||
data: Box<[u8]>,
|
data: Box<[u8]>,
|
||||||
header: Header,
|
header: Header,
|
||||||
}
|
}
|
||||||
|
|
@ -108,6 +109,7 @@ impl FlatState {
|
||||||
.collect(),
|
.collect(),
|
||||||
weapons: parsed.kills.into_iter().map(|kill| kill.weapon).collect(),
|
weapons: parsed.kills.into_iter().map(|kill| kill.weapon).collect(),
|
||||||
player_info: parsed.player_info,
|
player_info: parsed.player_info,
|
||||||
|
events: parsed.events,
|
||||||
header,
|
header,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -178,6 +180,16 @@ pub fn get_player_steam_id(state: &FlatState, player_id: usize) -> String {
|
||||||
state.player_info[player_id].steam_id.clone()
|
state.player_info[player_id].steam_id.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn get_event_count(state: &FlatState) -> usize {
|
||||||
|
state.events.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn get_event(state: &FlatState, id: usize) -> String {
|
||||||
|
serde_json::to_string(&state.events[id]).unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse_demo_inner(
|
pub fn parse_demo_inner(
|
||||||
buffer: &[u8],
|
buffer: &[u8],
|
||||||
progress: &Function,
|
progress: &Function,
|
||||||
|
|
@ -201,7 +213,7 @@ pub fn parse_demo_inner(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
parsed_demo.finish();
|
parsed_demo.finish(ticker.state());
|
||||||
|
|
||||||
let state = ticker.into_state();
|
let state = ticker.into_state();
|
||||||
|
|
||||||
|
|
|
||||||
78
src/state.rs
78
src/state.rs
|
|
@ -1,10 +1,12 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use tf_demo_parser::demo::data::game_state::{Projectile, ProjectileType};
|
use tf_demo_parser::demo::data::game_state::{Projectile, ProjectileType};
|
||||||
use tf_demo_parser::demo::data::DemoTick;
|
use tf_demo_parser::demo::data::DemoTick;
|
||||||
|
use tf_demo_parser::demo::gamevent::GameEvent;
|
||||||
use tf_demo_parser::demo::header::Header;
|
use tf_demo_parser::demo::header::Header;
|
||||||
use tf_demo_parser::demo::parser::analyser::UserInfo;
|
use tf_demo_parser::demo::parser::analyser::UserInfo;
|
||||||
use tf_demo_parser::demo::parser::gamestateanalyser::{
|
use tf_demo_parser::demo::parser::gamestateanalyser::{
|
||||||
Building, Class, Dispenser, GameState, Kill, PlayerState as PlayerAliveState, Sentry, Team,
|
Building, Class, Dispenser, GameState, Kill, PlayerState as PlayerAliveState, Sentry, Team,
|
||||||
Teleporter, World,
|
Teleporter, UserId, World,
|
||||||
};
|
};
|
||||||
use tf_demo_parser::demo::vector::VectorXY;
|
use tf_demo_parser::demo::vector::VectorXY;
|
||||||
|
|
||||||
|
|
@ -33,6 +35,7 @@ pub struct ParsedDemo {
|
||||||
pub buildings: Vec<Vec<u8>>,
|
pub buildings: Vec<Vec<u8>>,
|
||||||
pub projectiles: Vec<Vec<u8>>,
|
pub projectiles: Vec<Vec<u8>>,
|
||||||
pub kills: Vec<Kill>,
|
pub kills: Vec<Kill>,
|
||||||
|
pub events: Vec<SearchableEvent>,
|
||||||
pub header: Header,
|
pub header: Header,
|
||||||
pub player_info: Vec<UserInfo>,
|
pub player_info: Vec<UserInfo>,
|
||||||
pub max_building_count: usize,
|
pub max_building_count: usize,
|
||||||
|
|
@ -51,6 +54,7 @@ impl ParsedDemo {
|
||||||
player_info: Vec::new(),
|
player_info: Vec::new(),
|
||||||
max_building_count: 0,
|
max_building_count: 0,
|
||||||
max_projectile_count: 0,
|
max_projectile_count: 0,
|
||||||
|
events: Vec::new(),
|
||||||
header,
|
header,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -130,13 +134,19 @@ impl ParsedDemo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn finish(&mut self) {
|
pub fn finish(&mut self, state: &GameState) {
|
||||||
for parsed_building in self.buildings.iter_mut() {
|
for parsed_building in self.buildings.iter_mut() {
|
||||||
parsed_building.resize(self.tick * BuildingState::PACKET_SIZE, 0);
|
parsed_building.resize(self.tick * BuildingState::PACKET_SIZE, 0);
|
||||||
}
|
}
|
||||||
for parsed_projectiles in self.projectiles.iter_mut() {
|
for parsed_projectiles in self.projectiles.iter_mut() {
|
||||||
parsed_projectiles.resize(self.tick * ProjectileState::PACKET_SIZE, 0);
|
parsed_projectiles.resize(self.tick * ProjectileState::PACKET_SIZE, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.events = state
|
||||||
|
.events
|
||||||
|
.iter()
|
||||||
|
.flat_map(|(tick, event)| SearchableEvent::from_event(*tick, event))
|
||||||
|
.collect();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn size(&self) -> usize {
|
pub fn size(&self) -> usize {
|
||||||
|
|
@ -570,3 +580,67 @@ fn test_projectile_packing() {
|
||||||
assert!(f32::abs(input.position.x - unpacked.position.x) < 0.5);
|
assert!(f32::abs(input.position.x - unpacked.position.x) < 0.5);
|
||||||
assert!(f32::abs(input.position.y - unpacked.position.y) < 0.5);
|
assert!(f32::abs(input.position.y - unpacked.position.y) < 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||||
|
#[serde(rename_all = "lowercase")]
|
||||||
|
pub enum RawBuildingType {
|
||||||
|
Dispenser,
|
||||||
|
Teleporter,
|
||||||
|
SentryGun,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<u16> for RawBuildingType {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(value: u16) -> Result<Self, Self::Error> {
|
||||||
|
match value {
|
||||||
|
0 => Ok(RawBuildingType::Dispenser),
|
||||||
|
1 => Ok(RawBuildingType::Teleporter),
|
||||||
|
2 => Ok(RawBuildingType::SentryGun),
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||||
|
#[serde(tag = "type")]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum SearchableEvent {
|
||||||
|
Uber {
|
||||||
|
user_id: UserId,
|
||||||
|
target_id: UserId,
|
||||||
|
tick: DemoTick,
|
||||||
|
},
|
||||||
|
BuildingDestroyed {
|
||||||
|
attacker_id: UserId,
|
||||||
|
assister_id: UserId,
|
||||||
|
victim_id: UserId,
|
||||||
|
weapon: String,
|
||||||
|
building_type: RawBuildingType,
|
||||||
|
tick: DemoTick,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SearchableEvent {
|
||||||
|
pub fn from_event(tick: DemoTick, event: &GameEvent) -> Option<SearchableEvent> {
|
||||||
|
match event {
|
||||||
|
GameEvent::ObjectDestroyed(event) => {
|
||||||
|
let building_type = RawBuildingType::try_from(event.object_type).ok()?;
|
||||||
|
Some(SearchableEvent::BuildingDestroyed {
|
||||||
|
attacker_id: UserId::from(event.attacker),
|
||||||
|
assister_id: UserId::from(event.assister),
|
||||||
|
victim_id: UserId::from(event.user_id),
|
||||||
|
weapon: event.weapon.to_string(),
|
||||||
|
building_type,
|
||||||
|
tick,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
GameEvent::PlayerChargeDeployed(event) => Some(SearchableEvent::Uber {
|
||||||
|
user_id: UserId::from(event.user_id),
|
||||||
|
target_id: UserId::from(event.target_id),
|
||||||
|
tick,
|
||||||
|
}),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue