mirror of
https://codeberg.org/demostf/tf-demos-viewer.git
synced 2026-06-03 18:14:11 +02:00
playerstate packing
This commit is contained in:
parent
8c8e1c2f2e
commit
707695b45d
1 changed files with 114 additions and 17 deletions
131
src/state.rs
131
src/state.rs
|
|
@ -1,20 +1,21 @@
|
||||||
use std::ops::Index;
|
use std::ops::Index;
|
||||||
use tf_demo_parser::demo::parser::gamestateanalyser::{Class, GameState, Team};
|
use tf_demo_parser::demo::parser::gamestateanalyser::{Class, GameState, Team, World};
|
||||||
use tf_demo_parser::demo::vector::VectorXY;
|
use tf_demo_parser::demo::vector::{Vector, VectorXY};
|
||||||
use wasm_bindgen::prelude::*;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Default)]
|
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
||||||
pub struct Angle(u16);
|
pub struct Angle(u8);
|
||||||
|
|
||||||
impl From<f32> for Angle {
|
impl From<f32> for Angle {
|
||||||
fn from(val: f32) -> Self {
|
fn from(val: f32) -> Self {
|
||||||
Angle(val.rem_euclid(360.0) as u16)
|
let ratio = val.rem_euclid(360.0) / 360.0;
|
||||||
|
Angle((ratio * u8::max_value() as f32) as u8)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Angle> for u16 {
|
impl From<Angle> for f32 {
|
||||||
fn from(val: Angle) -> Self {
|
fn from(val: Angle) -> Self {
|
||||||
val.0
|
let ratio = val.0 as f32 / u8::max_value() as f32;
|
||||||
|
ratio * 360.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -58,6 +59,111 @@ impl ParsedDemo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone, PartialEq)]
|
||||||
|
pub struct PlayerState {
|
||||||
|
position: VectorXY,
|
||||||
|
angle: Angle,
|
||||||
|
health: u16,
|
||||||
|
team: Team,
|
||||||
|
class: Class,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlayerState {
|
||||||
|
pub fn pack(&self, world: &World) -> [u8; 8] {
|
||||||
|
// for the purpose of viewing the demo in the browser we dont really need high accuracy for
|
||||||
|
// position or angle, so we save a bunch of space by truncating those down to half the number
|
||||||
|
// of bits
|
||||||
|
fn pack_f32(val: f32, min: f32, max: f32) -> u16 {
|
||||||
|
let ratio = (val - min) / (max - min);
|
||||||
|
(ratio * u16::max_value() as f32) as u16
|
||||||
|
}
|
||||||
|
|
||||||
|
let x = pack_f32(self.position.x, world.boundary_min.x, world.boundary_max.x).to_le_bytes();
|
||||||
|
let y = pack_f32(self.position.y, world.boundary_min.y, world.boundary_max.y).to_le_bytes();
|
||||||
|
let team_and_class = ((self.team as u8) << 4) + self.class as u8;
|
||||||
|
let health_bytes = self.health.to_le_bytes();
|
||||||
|
|
||||||
|
[
|
||||||
|
x[0],
|
||||||
|
x[1],
|
||||||
|
y[0],
|
||||||
|
y[1],
|
||||||
|
health_bytes[0],
|
||||||
|
health_bytes[1],
|
||||||
|
self.angle.0,
|
||||||
|
team_and_class,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unpack(bytes: [u8; 8], world: &World) -> Self {
|
||||||
|
fn unpack_f32(val: u16, min: f32, max: f32) -> f32 {
|
||||||
|
let ratio = val as f32 / (u16::max_value() as f32);
|
||||||
|
ratio * (max - min) + min
|
||||||
|
}
|
||||||
|
|
||||||
|
let x = unpack_f32(
|
||||||
|
u16::from_le_bytes([bytes[0], bytes[1]]),
|
||||||
|
world.boundary_min.x,
|
||||||
|
world.boundary_max.x,
|
||||||
|
);
|
||||||
|
let y = unpack_f32(
|
||||||
|
u16::from_le_bytes([bytes[2], bytes[3]]),
|
||||||
|
world.boundary_min.y,
|
||||||
|
world.boundary_max.y,
|
||||||
|
);
|
||||||
|
let health = u16::from_le_bytes([bytes[4], bytes[5]]);
|
||||||
|
let angle = Angle(bytes[6]);
|
||||||
|
let team = Team::new(bytes[7] >> 4);
|
||||||
|
let class = Class::new(bytes[7] & 15);
|
||||||
|
|
||||||
|
PlayerState {
|
||||||
|
position: VectorXY { x, y },
|
||||||
|
angle,
|
||||||
|
health,
|
||||||
|
team,
|
||||||
|
class,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_packing() {
|
||||||
|
let world = World {
|
||||||
|
boundary_max: Vector {
|
||||||
|
x: 10000.0,
|
||||||
|
y: 10000.0,
|
||||||
|
z: 100.0,
|
||||||
|
},
|
||||||
|
boundary_min: Vector {
|
||||||
|
x: -10000.0,
|
||||||
|
y: -10000.0,
|
||||||
|
z: -100.0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let input = PlayerState {
|
||||||
|
position: VectorXY {
|
||||||
|
x: 100.0,
|
||||||
|
y: -5000.0,
|
||||||
|
},
|
||||||
|
angle: Angle::from(213.0),
|
||||||
|
health: 250,
|
||||||
|
team: Team::Blue,
|
||||||
|
class: Class::Demoman,
|
||||||
|
};
|
||||||
|
|
||||||
|
let bytes = input.pack(&world);
|
||||||
|
|
||||||
|
let unpacked = PlayerState::unpack(bytes, &world);
|
||||||
|
assert_eq!(input.angle, unpacked.angle);
|
||||||
|
assert_eq!(input.health, unpacked.health);
|
||||||
|
assert_eq!(input.class, unpacked.class);
|
||||||
|
assert_eq!(input.team, unpacked.team);
|
||||||
|
|
||||||
|
assert!(f32::abs(input.position.x - unpacked.position.x) < 0.5);
|
||||||
|
assert!(f32::abs(input.position.y - unpacked.position.y) < 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
pub struct ParsedPlayer {
|
pub struct ParsedPlayer {
|
||||||
position: Vec<VectorXY>,
|
position: Vec<VectorXY>,
|
||||||
|
|
@ -67,15 +173,6 @@ pub struct ParsedPlayer {
|
||||||
class: SparseVec<Class, 128>,
|
class: SparseVec<Class, 128>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone)]
|
|
||||||
pub struct PlayerState {
|
|
||||||
position: VectorXY,
|
|
||||||
angle: Angle,
|
|
||||||
health: u16,
|
|
||||||
team: Team,
|
|
||||||
class: Class,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ParsedPlayer {
|
impl ParsedPlayer {
|
||||||
fn push(
|
fn push(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue