diff --git a/src/node/src/lib.rs b/src/node/src/lib.rs index 735646b2..bc993ffb 100644 --- a/src/node/src/lib.rs +++ b/src/node/src/lib.rs @@ -6,6 +6,9 @@ use ahash::AHashMap; use memmap2::MmapOptions; use napi::bindgen_prelude::*; use napi::Either; +use napi::JsBigInt; +use napi::JsUnknown; +use parser::first_pass::parser_settings::rm_map_user_friendly_names; use parser::first_pass::parser_settings::rm_user_friendly_names; use parser::first_pass::parser_settings::ParserInputs; use parser::parse_demo::DemoOutput; @@ -14,6 +17,7 @@ use parser::second_pass::parser_settings::create_huffman_lookup_table; use parser::second_pass::variants::soa_to_aos; use parser::second_pass::variants::BytesVariant; use parser::second_pass::variants::OutputSerdeHelperStruct; +use parser::second_pass::variants::Variant; use parser::second_pass::voice_data::convert_voice_data_to_wav; use serde_json::Value; use std::collections::HashMap; @@ -21,6 +25,92 @@ use std::fs::File; use std::hash::RandomState; use std::result::Result; +#[napi] +#[derive(Clone)] +pub struct JsVariant(Variant); + +impl FromNapiValue for JsVariant { + unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> napi::Result { + let js_unknown = JsUnknown::from_napi_value(env, napi_val)?; + + match js_unknown.get_type() { + Ok(js_unknown_type) => { + if js_unknown_type == ValueType::Boolean { + if let Ok(val) = js_unknown.coerce_to_bool() { + Ok(JsVariant(Variant::Bool(val.get_value()?))) + } else { + Err(Error::new( + Status::InvalidArg, + "Unspported Boolean type for Variant".to_owned(), + )) + } + } else if js_unknown_type == ValueType::String { + if let Ok(val) = js_unknown.coerce_to_string() { + Ok(JsVariant(Variant::String(val.into_utf8()?.into_owned()?))) + } else { + Err(Error::new( + Status::InvalidArg, + "Unsupported String for Variant".to_owned(), + )) + } + } else if js_unknown_type == ValueType::Number { + if let Ok(val) = js_unknown.coerce_to_number() { + let num = val.get_double()?; + if num.fract() == 0.0 { + if let Ok(val) = val.get_uint32() { + Ok(JsVariant(Variant::U32(val))) + } else if let Ok(val) = val.get_int32() { + let int32_val = val; + if int32_val >= i16::MIN as i32 && int32_val <= i16::MAX as i32 { + Ok(JsVariant(Variant::I16(int32_val as i16))) + } else { + Ok(JsVariant(Variant::I32(int32_val))) + } + } else { + Err(Error::new( + Status::InvalidArg, + "Unsupported number type".to_owned(), + )) + } + } else { + Ok(JsVariant(Variant::F32(num as f32))) + } + } else { + Err(Error::new( + Status::InvalidArg, + "Unsupported number type".to_owned(), + )) + } + } else if js_unknown_type == ValueType::BigInt { + let bigint_val = js_unknown.cast::(); + match bigint_val.get_u64() { + Ok((val, true)) => Ok(JsVariant(Variant::U64(val))), + _ => Err(Error::new( + Status::InvalidArg, + "Unsupported number type".to_owned(), + )), + } + } else { + Err(Error::new( + Status::InvalidArg, + "Unspported type for Variant".to_owned(), + )) + } + } + _ => Err(Error::new( + Status::InvalidArg, + "Unspported type for Variant".to_owned(), + )), + } + } +} + +#[napi] +pub struct WantedPropState { + pub prop: String, + pub state: JsVariant, +} + fn parse_demo(bytes: BytesVariant, parser: &mut Parser) -> Result { match bytes { BytesVariant::Mmap(m) => match parser.parse_demo(&m) { @@ -42,6 +132,7 @@ pub fn parse_voice(path_or_buf: Either) -> napi::Result) -> napi::Result) -> napi::Result) -> napi::Result wanted_players: vec![], wanted_player_props: vec![], wanted_other_props: vec![], + wanted_prop_states: AHashMap::default(), wanted_events: vec![], parse_ents: false, wanted_ticks: vec![], @@ -198,6 +292,7 @@ pub fn parse_event( wanted_players: vec![], wanted_player_props: real_names_player.clone(), wanted_other_props: real_other_props, + wanted_prop_states: AHashMap::default(), wanted_events: vec![event_name], parse_ents: true, wanted_ticks: vec![], @@ -260,6 +355,7 @@ pub fn parse_events( wanted_players: vec![], wanted_player_props: real_names_player.clone(), wanted_other_props: real_other_props.clone(), + wanted_prop_states: AHashMap::default(), wanted_events: event_names, parse_ents: true, wanted_ticks: vec![], @@ -285,6 +381,7 @@ pub fn parse_ticks( wanted_props: Vec, wanted_ticks: Option>, wanted_players: Option>, + prop_states: Option>, struct_of_arrays: Option, order_by_steamid: Option, ) -> napi::Result { @@ -296,6 +393,16 @@ pub fn parse_ticks( Some(v) => v.iter().map(|x| x.parse::().unwrap_or(0)).collect(), None => vec![], }; + let wanted_prop_states: AHashMap = prop_states + .unwrap_or_default() + .into_iter() + .map(|prop| (prop.prop.clone(), prop.state.0.clone())) + .collect(); + let real_wanted_prop_states = rm_map_user_friendly_names(&wanted_prop_states); + let real_wanted_prop_states = match real_wanted_prop_states { + Ok(real_wanted_prop_states) => real_wanted_prop_states, + Err(e) => return Err(Error::new(Status::InvalidArg, format!("{}", e).to_owned())), + }; let bytes = resolve_byte_type(path_or_buf)?; let huf = create_huffman_lookup_table(); @@ -304,6 +411,12 @@ pub fn parse_ticks( for (real_name, user_friendly_name) in real_names.iter().zip(&wanted_props) { real_name_to_og_name.insert(real_name.clone(), user_friendly_name.clone()); } + for (real_name, user_friendly_name) in real_wanted_prop_states + .keys() + .zip(wanted_prop_states.keys()) + { + real_name_to_og_name.insert(real_name.clone(), user_friendly_name.clone()); + } let wanted_ticks = match wanted_ticks { Some(t) => t, @@ -320,6 +433,7 @@ pub fn parse_ticks( wanted_player_props: real_names.clone(), wanted_other_props: vec![], wanted_events: vec![], + wanted_prop_states: real_wanted_prop_states, parse_ents: true, wanted_ticks: wanted_ticks, parse_projectiles: false, @@ -391,6 +505,7 @@ pub fn parse_player_info(path_or_buf: Either) -> napi::Result) -> napi::Result