plasma_prp/core/
synched_object.rs1use std::io::Read;
9
10use anyhow::Result;
11
12use crate::resource::prp::PlasmaRead;
13
14#[allow(dead_code)]
16pub mod synch_flags {
17 pub const DONT_DIRTY: u32 = 0x1;
18 pub const SEND_RELIABLY: u32 = 0x2;
19 pub const HAS_CONSTANT_NET_GROUP: u32 = 0x4;
20 pub const DONT_SYNCH_GAME_MESSAGES: u32 = 0x8;
21 pub const EXCLUDE_PERSISTENT_STATE: u32 = 0x10;
22 pub const EXCLUDE_ALL_PERSISTENT_STATE: u32 = 0x20;
23 pub const LOCAL_ONLY: u32 = EXCLUDE_ALL_PERSISTENT_STATE | DONT_SYNCH_GAME_MESSAGES;
24 pub const HAS_VOLATILE_STATE: u32 = 0x40;
25 pub const ALL_STATE_IS_VOLATILE: u32 = 0x80;
26}
27
28#[allow(dead_code)]
30pub mod sdl_send_flags {
31 pub const BCAST_TO_CLIENTS: u32 = 0x1;
32 pub const FORCE_FULL_SEND: u32 = 0x2;
33 pub const SKIP_LOCAL_OWNERSHIP_CHECK: u32 = 0x4;
34 pub const SEND_IMMEDIATELY: u32 = 0x8;
35 pub const DONT_PERSIST_ON_SERVER: u32 = 0x10;
36 pub const USE_RELEVANCE_REGIONS: u32 = 0x20;
37 pub const NEW_STATE: u32 = 0x40;
38 pub const IS_AVATAR_STATE: u32 = 0x80;
39}
40
41#[derive(Debug, Clone, Default)]
43pub struct SynchedObjectData {
44 pub synch_flags: u32,
45 pub sdl_exclude_list: Vec<String>,
46 pub sdl_volatile_list: Vec<String>,
47}
48
49impl SynchedObjectData {
50 pub fn read(reader: &mut impl Read) -> Result<Self> {
54 let synch_flags = reader.read_u32()?;
55
56 let mut sdl_exclude_list = Vec::new();
57 if synch_flags & synch_flags::EXCLUDE_PERSISTENT_STATE != 0 {
58 let count = reader.read_u16()?;
59 for _ in 0..count {
60 sdl_exclude_list.push(reader.read_safe_string()?);
61 }
62 }
63
64 let mut sdl_volatile_list = Vec::new();
65 if synch_flags & synch_flags::HAS_VOLATILE_STATE != 0 {
66 let count = reader.read_u16()?;
67 for _ in 0..count {
68 sdl_volatile_list.push(reader.read_safe_string()?);
69 }
70 }
71
72 Ok(Self {
73 synch_flags,
74 sdl_exclude_list,
75 sdl_volatile_list,
76 })
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83 use std::io::Cursor;
84
85 #[test]
86 fn test_read_simple() {
87 let data = [0x00, 0x00, 0x00, 0x00];
89 let mut cursor = Cursor::new(&data);
90 let synched = SynchedObjectData::read(&mut cursor).unwrap();
91 assert_eq!(synched.synch_flags, 0);
92 assert!(synched.sdl_exclude_list.is_empty());
93 assert!(synched.sdl_volatile_list.is_empty());
94 }
95
96 #[test]
98 fn test_parse_cleft_scene_objects() {
99 use crate::resource::prp::{PrpPage, class_types};
100 use crate::core::uoid::read_key_uoid;
101 use std::path::Path;
102
103 let path = Path::new("../../Plasma/staging/client/dat/Cleft_District_Cleft.prp");
104 if !path.exists() {
105 eprintln!("Skipping test: {:?} not found", path);
106 return;
107 }
108
109 let page = PrpPage::from_file(path).unwrap();
110 let scene_keys: Vec<_> = page.keys_of_type(class_types::PL_SCENE_OBJECT);
111
112 let mut parsed = 0;
113 let mut failed = 0;
114 for key in &scene_keys {
115 if let Some(data) = page.object_data(key) {
116 let mut cursor = Cursor::new(data);
117
118 let class_idx = cursor.read_i16().unwrap();
120 if class_idx < 0 {
121 continue;
122 }
123
124 match read_key_uoid(&mut cursor) {
126 Ok(Some(uoid)) => {
127 assert_eq!(uoid.object_name, key.object_name);
128 }
129 Ok(None) => continue,
130 Err(_) => {
131 failed += 1;
132 continue;
133 }
134 }
135
136 match SynchedObjectData::read(&mut cursor) {
138 Ok(synched) => {
139 parsed += 1;
140 }
143 Err(e) => {
144 failed += 1;
145 eprintln!("Failed to parse synched object for {}: {}", key.object_name, e);
146 }
147 }
148 }
149 }
150
151 eprintln!(
152 "Parsed {}/{} plSceneObject synched headers ({} failed)",
153 parsed,
154 scene_keys.len(),
155 failed
156 );
157 assert!(parsed > 0, "Should have parsed at least some scene objects");
158 assert_eq!(failed, 0, "No synched object parses should fail");
159 }
160}