pub(crate) use self::attributes::*;
pub use self::models::*;
pub(crate) use self::object_index::*;
pub mod attributes;
mod frame_decoder;
mod models;
mod object_index;
use crate::data::{ATTRIBUTES, SPAWN_STATS};
use crate::errors::NetworkError;
use crate::header::Header;
use crate::models::*;
use crate::network::frame_decoder::FrameDecoder;
use crate::parser::ReplayBody;
use fnv::FnvHashMap;
use frame_decoder::SegmentedArray;
use std::cmp;
#[derive(PartialEq, Debug, Clone)]
pub(crate) struct CacheInfo {
pub(crate) max_prop_id: u32,
pub(crate) prop_id_bits: u32,
pub(crate) attributes: SegmentedArray<StreamId, ObjectAttribute>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct ObjectAttribute {
pub(crate) attribute: AttributeTag,
pub(crate) object_id: ObjectId,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) struct VersionTriplet(pub i32, pub i32, pub i32);
impl VersionTriplet {
pub fn net_version(&self) -> i32 {
self.2
}
}
pub(crate) fn parse(header: &Header, body: &ReplayBody) -> Result<NetworkFrames, NetworkError> {
let version = VersionTriplet(
header.major_version,
header.minor_version,
header.net_version.unwrap_or(0),
);
let object_index = object_index::ObjectIndex::new(&body.objects);
let mut spawns: Vec<Option<SpawnTrajectory>> = vec![None; body.objects.len()];
for (object_name, spawn) in SPAWN_STATS.iter() {
let Some(id) = object_index.by_name(object_name) else {
continue;
};
spawns[id.0 as usize] = Some(*spawn);
}
let mut parent_stack = Vec::with_capacity(body.objects.len());
for name in &body.objects {
let mut result = SpawnTrajectory::None;
for object in object_index.hierarchy(name) {
match spawns[object.0 as usize] {
Some(spawn) => {
result = spawn;
break;
}
None => parent_stack.push(object),
}
}
for ind in parent_stack.drain(..) {
spawns[ind.0 as usize] = Some(result)
}
}
let mut net_properties: FnvHashMap<ObjectId, Vec<(_, _)>> =
FnvHashMap::with_capacity_and_hasher(body.net_cache.len(), Default::default());
for cache in &body.net_cache {
let key = ObjectId(cache.object_ind);
let properties = net_properties.entry(key).or_default();
properties.reserve(cache.properties.len());
for x in &cache.properties {
let attr = body
.objects
.get(x.object_ind as usize)
.map(|x| {
ATTRIBUTES
.get(x)
.copied()
.unwrap_or(AttributeTag::NotImplemented)
})
.ok_or(NetworkError::StreamTooLargeIndex(x.stream_id, x.object_ind))?;
properties.push((
StreamId(x.stream_id),
ObjectAttribute {
attribute: attr,
object_id: ObjectId(x.object_ind),
},
));
}
}
let mut object_ind_attrs: FnvHashMap<ObjectId, FnvHashMap<StreamId, ObjectAttribute>> =
FnvHashMap::with_capacity_and_hasher(body.objects.len(), Default::default());
let mut acc_attrs = Vec::with_capacity(32);
for name in &body.objects {
net_traversal(
name.as_str(),
&mut acc_attrs,
&mut parent_stack,
&net_properties,
&object_index,
&mut object_ind_attrs,
);
}
let mut object_ind_attributes: Vec<Option<CacheInfo>> = Vec::with_capacity(body.objects.len());
object_ind_attributes.resize_with(body.objects.len(), || None);
let iter = object_ind_attrs.into_iter().map(|(obj_id, attrs)| {
let id = obj_id;
let max = attrs
.keys()
.map(|&x| i32::from(x))
.max()
.unwrap_or(2)
.saturating_add(1);
let mut attributes = SegmentedArray::new(64);
for (k, v) in attrs {
attributes.insert(k, v);
}
let max_bit_width = crate::bits::bit_width(max as u64);
Ok((
id,
CacheInfo {
max_prop_id: max as u32,
prop_id_bits: cmp::max(max_bit_width, 1) - 1,
attributes,
},
))
});
for x in iter {
let (object, cache) = x?;
object_ind_attributes[object.0 as usize] = Some(cache);
}
let product_decoder = ProductValueDecoder::create(version, &object_index);
let max_channels = header.max_channels().unwrap_or(1023) as u32;
let channel_width = crate::bits::bit_width(u64::from(max_channels)).saturating_sub(1);
let channel_bits = cmp::max(channel_width, 0);
let num_frames = header.num_frames();
let is_lan = header.match_type().map(|x| x == "Lan").unwrap_or(false);
let is_rl_223 = matches!(header.build_version(), Some(x) if x >= "221120.42953.406184");
if let Some(frame_len) = num_frames {
if frame_len as usize > body.network_data.len() {
return Err(NetworkError::TooManyFrames(frame_len));
}
let spawns = spawns
.drain(..)
.map(|x| x.unwrap_or(SpawnTrajectory::None))
.collect();
let frame_decoder = FrameDecoder {
frames_len: frame_len as usize,
product_decoder,
max_channels,
channel_bits,
body,
spawns: &spawns,
object_ind_attributes,
version,
is_lan,
is_rl_223,
};
Ok(NetworkFrames {
frames: frame_decoder.decode_frames()?,
})
} else {
Ok(NetworkFrames { frames: Vec::new() })
}
}
fn net_traversal(
object_name: &str,
acc_attrs: &mut Vec<(StreamId, ObjectAttribute)>,
parent_stack: &mut Vec<ObjectId>,
net_properties: &FnvHashMap<ObjectId, Vec<(StreamId, ObjectAttribute)>>,
object_index: &object_index::ObjectIndex,
object_ind_attrs: &mut FnvHashMap<ObjectId, FnvHashMap<StreamId, ObjectAttribute>>,
) {
acc_attrs.clear();
for object in object_index.hierarchy(object_name) {
match object_ind_attrs.get(&object) {
Some(attrs) => {
acc_attrs.extend(attrs.iter().map(|(x, y)| (*x, *y)));
break;
}
None => parent_stack.push(object),
}
}
let mut cache_attrs = FnvHashMap::default();
for ind in parent_stack.drain(..).rev() {
let attrs = net_properties.get(&ind).into_iter().flatten().copied();
acc_attrs.extend(attrs);
cache_attrs.clear();
cache_attrs.extend(acc_attrs.iter().cloned());
object_ind_attrs.insert(ind, cache_attrs.clone());
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_version_triplets() {
let version = VersionTriplet(18, 27, 1);
assert_eq!(version.net_version(), 1);
assert!(version < VersionTriplet(19, 27, 1));
assert!(version < VersionTriplet(18, 28, 1));
assert!(version < VersionTriplet(18, 27, 2));
assert_eq!(version, VersionTriplet(18, 27, 1));
assert!(version > VersionTriplet(17, 27, 1));
assert!(version > VersionTriplet(18, 26, 1));
assert!(version > VersionTriplet(18, 27, 0));
}
}