use std::io::Read;
use anyhow::Result;
use crate::resource::prp::PlasmaRead;
#[allow(dead_code)]
pub mod synch_flags {
pub const DONT_DIRTY: u32 = 0x1;
pub const SEND_RELIABLY: u32 = 0x2;
pub const HAS_CONSTANT_NET_GROUP: u32 = 0x4;
pub const DONT_SYNCH_GAME_MESSAGES: u32 = 0x8;
pub const EXCLUDE_PERSISTENT_STATE: u32 = 0x10;
pub const EXCLUDE_ALL_PERSISTENT_STATE: u32 = 0x20;
pub const LOCAL_ONLY: u32 = EXCLUDE_ALL_PERSISTENT_STATE | DONT_SYNCH_GAME_MESSAGES;
pub const HAS_VOLATILE_STATE: u32 = 0x40;
pub const ALL_STATE_IS_VOLATILE: u32 = 0x80;
}
#[allow(dead_code)]
pub mod sdl_send_flags {
pub const BCAST_TO_CLIENTS: u32 = 0x1;
pub const FORCE_FULL_SEND: u32 = 0x2;
pub const SKIP_LOCAL_OWNERSHIP_CHECK: u32 = 0x4;
pub const SEND_IMMEDIATELY: u32 = 0x8;
pub const DONT_PERSIST_ON_SERVER: u32 = 0x10;
pub const USE_RELEVANCE_REGIONS: u32 = 0x20;
pub const NEW_STATE: u32 = 0x40;
pub const IS_AVATAR_STATE: u32 = 0x80;
}
#[derive(Debug, Clone, Default)]
pub struct SynchedObjectData {
pub synch_flags: u32,
pub sdl_exclude_list: Vec<String>,
pub sdl_volatile_list: Vec<String>,
}
impl SynchedObjectData {
pub fn read(reader: &mut impl Read) -> Result<Self> {
let synch_flags = reader.read_u32()?;
let mut sdl_exclude_list = Vec::new();
if synch_flags & synch_flags::EXCLUDE_PERSISTENT_STATE != 0 {
let count = reader.read_u16()?;
for _ in 0..count {
sdl_exclude_list.push(reader.read_safe_string()?);
}
}
let mut sdl_volatile_list = Vec::new();
if synch_flags & synch_flags::HAS_VOLATILE_STATE != 0 {
let count = reader.read_u16()?;
for _ in 0..count {
sdl_volatile_list.push(reader.read_safe_string()?);
}
}
Ok(Self {
synch_flags,
sdl_exclude_list,
sdl_volatile_list,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Cursor;
#[test]
fn test_read_simple() {
let data = [0x00, 0x00, 0x00, 0x00];
let mut cursor = Cursor::new(&data);
let synched = SynchedObjectData::read(&mut cursor).unwrap();
assert_eq!(synched.synch_flags, 0);
assert!(synched.sdl_exclude_list.is_empty());
assert!(synched.sdl_volatile_list.is_empty());
}
#[test]
fn test_parse_cleft_scene_objects() {
use crate::resource::prp::{PrpPage, class_types};
use crate::core::uoid::read_key_uoid;
use std::path::Path;
let path = Path::new("../../Plasma/staging/client/dat/Cleft_District_Cleft.prp");
if !path.exists() {
eprintln!("Skipping test: {:?} not found", path);
return;
}
let page = PrpPage::from_file(path).unwrap();
let scene_keys: Vec<_> = page.keys_of_type(class_types::PL_SCENE_OBJECT);
let mut parsed = 0;
let mut failed = 0;
for key in &scene_keys {
if let Some(data) = page.object_data(key) {
let mut cursor = Cursor::new(data);
let class_idx = cursor.read_i16().unwrap();
if class_idx < 0 {
continue;
}
match read_key_uoid(&mut cursor) {
Ok(Some(uoid)) => {
assert_eq!(uoid.object_name, key.object_name);
}
Ok(None) => continue,
Err(_) => {
failed += 1;
continue;
}
}
match SynchedObjectData::read(&mut cursor) {
Ok(synched) => {
parsed += 1;
}
Err(e) => {
failed += 1;
eprintln!("Failed to parse synched object for {}: {}", key.object_name, e);
}
}
}
}
eprintln!(
"Parsed {}/{} plSceneObject synched headers ({} failed)",
parsed,
scene_keys.len(),
failed
);
assert!(parsed > 0, "Should have parsed at least some scene objects");
assert_eq!(failed, 0, "No synched object parses should fail");
}
}