mirror of
https://codeberg.org/demostf/tf-demos-viewer.git
synced 2026-06-03 18:14:11 +02:00
state stuff
This commit is contained in:
parent
dd26cc9bce
commit
5031746e8b
2 changed files with 166 additions and 12 deletions
43
src/lib.rs
43
src/lib.rs
|
|
@ -1,26 +1,39 @@
|
||||||
use tf_demo_parser::demo::parser::gamestateanalyser::GameStateAnalyser;
|
#![feature(const_generics)]
|
||||||
use tf_demo_parser::{Demo, DemoParser, ParseError};
|
#![macro_use]
|
||||||
use wasm_bindgen::__rt::std::time::Instant;
|
|
||||||
use wasm_bindgen::prelude::*;
|
|
||||||
use web_sys::console;
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
use crate::state::ParsedDemo;
|
||||||
pub fn parse_demo(buffer: &[u8]) -> Result<(), JsValue> {
|
use tf_demo_parser::demo::parser::gamestateanalyser::{GameState, GameStateAnalyser};
|
||||||
let buffer = buffer.to_vec();
|
use tf_demo_parser::{Demo, DemoParser, ParseError};
|
||||||
parse_demo_inner(buffer).map_err(|e| e.to_string().into())
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
mod state;
|
||||||
|
|
||||||
|
macro_rules! log {
|
||||||
|
($($arg:tt)*) => (web_sys::console::log_1(&JsValue::from(format!($($arg)*))))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_demo_inner(buffer: Vec<u8>) -> Result<(), ParseError> {
|
#[wasm_bindgen]
|
||||||
console::log_1(&JsValue::from_str(&format!("len: {}", buffer.len())));
|
pub fn parse_demo(buffer: Box<[u8]>) -> Result<(), JsValue> {
|
||||||
|
let buffer = buffer.into_vec();
|
||||||
|
let parsed = parse_demo_inner(buffer).map_err(|e| JsValue::from(e.to_string()))?;
|
||||||
|
|
||||||
|
log!("{:?}", parsed.players[2].get(10));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_demo_inner(buffer: Vec<u8>) -> Result<ParsedDemo, ParseError> {
|
||||||
let demo = Demo::new(buffer);
|
let demo = Demo::new(buffer);
|
||||||
let parser = DemoParser::new_with_analyser(demo.get_stream(), GameStateAnalyser::default());
|
let parser = DemoParser::new_with_analyser(demo.get_stream(), GameStateAnalyser::default());
|
||||||
let (header, mut ticker) = parser.ticker()?;
|
let (header, mut ticker) = parser.ticker()?;
|
||||||
|
|
||||||
|
let mut parsed_demo = ParsedDemo::new();
|
||||||
|
|
||||||
while ticker.tick()? {
|
while ticker.tick()? {
|
||||||
// noop
|
parsed_demo.push_state(ticker.state());
|
||||||
}
|
}
|
||||||
|
|
||||||
console::log_1(&JsValue::from_str(&format!("{:?}", header)));
|
Ok(parsed_demo)
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is like the `main` function, except for JavaScript.
|
// This is like the `main` function, except for JavaScript.
|
||||||
|
|
@ -32,7 +45,7 @@ pub fn main_js() -> Result<(), JsValue> {
|
||||||
console_error_panic_hook::set_once();
|
console_error_panic_hook::set_once();
|
||||||
|
|
||||||
// Your code goes here!
|
// Your code goes here!
|
||||||
console::log_1(&JsValue::from_str("Hello world!"));
|
log!("Hello world!");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
141
src/state.rs
Normal file
141
src/state.rs
Normal file
|
|
@ -0,0 +1,141 @@
|
||||||
|
use std::ops::Index;
|
||||||
|
use tf_demo_parser::demo::parser::gamestateanalyser::{Class, GameState, Team};
|
||||||
|
use tf_demo_parser::demo::vector::VectorXY;
|
||||||
|
|
||||||
|
macro_rules! log {
|
||||||
|
($($arg:tt)*) => (web_sys::console::log_1(&wasm_bindgen::prelude::JsValue::from(format!($($arg)*))))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
|
pub struct Angle(u16);
|
||||||
|
|
||||||
|
impl From<f32> for Angle {
|
||||||
|
fn from(val: f32) -> Self {
|
||||||
|
Angle(val.rem_euclid(360.0) as u16)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Angle> for u16 {
|
||||||
|
fn from(val: Angle) -> Self {
|
||||||
|
val.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct ParsedDemo {
|
||||||
|
tick: usize,
|
||||||
|
pub players: Vec<ParsedPlayer>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ParsedDemo {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_state(&mut self, game_state: &GameState) {
|
||||||
|
for (index, player) in game_state.players.iter().enumerate() {
|
||||||
|
if let None = self.players.get(index) {
|
||||||
|
let mut new_player = ParsedPlayer::default();
|
||||||
|
// backfill with defaults
|
||||||
|
new_player.resize(self.tick);
|
||||||
|
self.players.push(new_player)
|
||||||
|
};
|
||||||
|
|
||||||
|
let parsed_player = &mut self.players[index];
|
||||||
|
parsed_player.push(
|
||||||
|
self.tick,
|
||||||
|
player.position.into(),
|
||||||
|
player.view_angle.into(),
|
||||||
|
player.health,
|
||||||
|
player.team,
|
||||||
|
player.class,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
self.tick += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone)]
|
||||||
|
pub struct ParsedPlayer {
|
||||||
|
position: Vec<VectorXY>,
|
||||||
|
angle: SparseVec<Angle, 1>,
|
||||||
|
health: SparseVec<u16, 4>,
|
||||||
|
team: SparseVec<Team, 128>,
|
||||||
|
class: SparseVec<Class, 128>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone)]
|
||||||
|
pub struct PlayerState {
|
||||||
|
position: VectorXY,
|
||||||
|
angle: Angle,
|
||||||
|
health: u16,
|
||||||
|
team: Team,
|
||||||
|
class: Class,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ParsedPlayer {
|
||||||
|
fn push(
|
||||||
|
&mut self,
|
||||||
|
index: usize,
|
||||||
|
position: VectorXY,
|
||||||
|
angle: Angle,
|
||||||
|
health: u16,
|
||||||
|
team: Team,
|
||||||
|
class: Class,
|
||||||
|
) {
|
||||||
|
debug_assert!(self.position.len() == index);
|
||||||
|
self.position.push(position);
|
||||||
|
|
||||||
|
self.angle.push_index(index, angle);
|
||||||
|
self.health.push_index(index, health);
|
||||||
|
self.team.push_index(index, team);
|
||||||
|
self.class.push_index(index, class);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resize(&mut self, size: usize) {
|
||||||
|
self.position.resize_with(size, || VectorXY::default());
|
||||||
|
self.angle.resize(size);
|
||||||
|
self.health.resize(size);
|
||||||
|
self.team.resize(size);
|
||||||
|
self.class.resize(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.position.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, index: usize) -> PlayerState {
|
||||||
|
PlayerState {
|
||||||
|
position: self.position[index],
|
||||||
|
angle: self.angle[index],
|
||||||
|
health: self.health[index],
|
||||||
|
team: self.team[index],
|
||||||
|
class: self.class[index],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone)]
|
||||||
|
pub struct SparseVec<T: Default, const N: usize> {
|
||||||
|
inner: Vec<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Default, const N: usize> SparseVec<T, N> {
|
||||||
|
fn push_index(&mut self, index: usize, val: T) {
|
||||||
|
if index % N == 0 {
|
||||||
|
self.inner.push(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resize(&mut self, size: usize) {
|
||||||
|
self.inner.resize_with(size / N, Default::default)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Default, const N: usize> Index<usize> for SparseVec<T, N> {
|
||||||
|
type Output = T;
|
||||||
|
|
||||||
|
fn index(&self, index: usize) -> &Self::Output {
|
||||||
|
self.inner.index(index / N)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue