tx2_pack/
format.rs

1use serde::{Deserialize, Serialize};
2use tx2_link::{EntityId, ComponentId};
3use ahash::AHashMap;
4use std::collections::HashMap;
5
6pub const MAGIC_NUMBER: &[u8; 8] = b"TX2PACK\0";
7pub const FORMAT_VERSION: u32 = 1;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
10pub enum PackFormat {
11    Bincode,
12    MessagePack,
13    Custom,
14}
15
16#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct SnapshotHeader {
18    pub magic: [u8; 8],
19    pub version: u32,
20    pub format: PackFormat,
21    pub compression: CompressionType,
22    pub encrypted: bool,
23    pub checksum: [u8; 32],
24    pub timestamp: i64,
25    pub entity_count: u64,
26    pub component_count: u64,
27    pub archetype_count: u64,
28    pub data_offset: u64,
29    pub data_size: u64,
30    pub metadata_offset: u64,
31    pub metadata_size: u64,
32}
33
34impl SnapshotHeader {
35    pub fn new() -> Self {
36        Self {
37            magic: *MAGIC_NUMBER,
38            version: FORMAT_VERSION,
39            format: PackFormat::Bincode,
40            compression: CompressionType::Zstd,
41            encrypted: false,
42            checksum: [0u8; 32],
43            timestamp: chrono::Utc::now().timestamp(),
44            entity_count: 0,
45            component_count: 0,
46            archetype_count: 0,
47            data_offset: 0,
48            data_size: 0,
49            metadata_offset: 0,
50            metadata_size: 0,
51        }
52    }
53
54    pub fn validate(&self) -> crate::Result<()> {
55        if self.magic != *MAGIC_NUMBER {
56            return Err(crate::PackError::InvalidFormat(
57                "Invalid magic number".to_string()
58            ));
59        }
60
61        if self.version != FORMAT_VERSION {
62            return Err(crate::PackError::VersionMismatch {
63                expected: FORMAT_VERSION.to_string(),
64                actual: self.version.to_string(),
65            });
66        }
67
68        Ok(())
69    }
70}
71
72impl Default for SnapshotHeader {
73    fn default() -> Self {
74        Self::new()
75    }
76}
77
78#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
79pub enum CompressionType {
80    None,
81    Zstd,
82    Lz4,
83}
84
85#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct ComponentArchetype {
87    pub component_id: ComponentId,
88    pub entity_ids: Vec<EntityId>,
89    pub data: ComponentData,
90}
91
92#[derive(Debug, Clone, Serialize, Deserialize)]
93pub enum ComponentData {
94    StructOfArrays(StructOfArraysData),
95    Blob(Vec<u8>),
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize)]
99pub struct StructOfArraysData {
100    pub field_names: Vec<String>,
101    pub field_types: Vec<FieldType>,
102    pub field_data: Vec<FieldArray>,
103}
104
105#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
106pub enum FieldType {
107    Bool,
108    I8,
109    I16,
110    I32,
111    I64,
112    U8,
113    U16,
114    U32,
115    U64,
116    F32,
117    F64,
118    String,
119    Bytes,
120}
121
122#[derive(Debug, Clone, Serialize, Deserialize)]
123pub enum FieldArray {
124    Bool(Vec<bool>),
125    I8(Vec<i8>),
126    I16(Vec<i16>),
127    I32(Vec<i32>),
128    I64(Vec<i64>),
129    U8(Vec<u8>),
130    U16(Vec<u16>),
131    U32(Vec<u32>),
132    U64(Vec<u64>),
133    F32(Vec<f32>),
134    F64(Vec<f64>),
135    String(Vec<String>),
136    Bytes(Vec<Vec<u8>>),
137}
138
139impl FieldArray {
140    pub fn len(&self) -> usize {
141        match self {
142            FieldArray::Bool(v) => v.len(),
143            FieldArray::I8(v) => v.len(),
144            FieldArray::I16(v) => v.len(),
145            FieldArray::I32(v) => v.len(),
146            FieldArray::I64(v) => v.len(),
147            FieldArray::U8(v) => v.len(),
148            FieldArray::U16(v) => v.len(),
149            FieldArray::U32(v) => v.len(),
150            FieldArray::U64(v) => v.len(),
151            FieldArray::F32(v) => v.len(),
152            FieldArray::F64(v) => v.len(),
153            FieldArray::String(v) => v.len(),
154            FieldArray::Bytes(v) => v.len(),
155        }
156    }
157
158    pub fn is_empty(&self) -> bool {
159        self.len() == 0
160    }
161}
162
163#[derive(Debug, Clone, Serialize, Deserialize)]
164pub struct PackedSnapshot {
165    pub header: SnapshotHeader,
166    pub archetypes: Vec<ComponentArchetype>,
167    pub entity_metadata: HashMap<EntityId, EntityMetadata>,
168}
169
170#[derive(Debug, Clone, Serialize, Deserialize)]
171pub struct EntityMetadata {
172    pub created_at: i64,
173    pub modified_at: i64,
174    pub tags: Vec<String>,
175}
176
177impl PackedSnapshot {
178    pub fn new() -> Self {
179        Self {
180            header: SnapshotHeader::new(),
181            archetypes: Vec::new(),
182            entity_metadata: HashMap::new(),
183        }
184    }
185
186    pub fn from_world_snapshot(snapshot: tx2_link::WorldSnapshot) -> Self {
187        let mut packed = Self::new();
188        packed.header.timestamp = snapshot.timestamp as i64;
189
190        let entity_count = snapshot.entities.len() as u64;
191
192        let mut component_map: AHashMap<ComponentId, ComponentArchetype> = AHashMap::new();
193
194        for entity in &snapshot.entities {
195            for component in &entity.components {
196                let archetype = component_map
197                    .entry(component.id.clone())
198                    .or_insert_with(|| ComponentArchetype {
199                        component_id: component.id.clone(),
200                        entity_ids: Vec::new(),
201                        data: ComponentData::Blob(Vec::new()),
202                    });
203
204                archetype.entity_ids.push(entity.id);
205            }
206        }
207
208        packed.archetypes = component_map.into_values().collect();
209        packed.header.entity_count = entity_count;
210        packed.header.component_count = packed.archetypes.len() as u64;
211        packed.header.archetype_count = packed.archetypes.len() as u64;
212
213        packed
214    }
215}
216
217impl Default for PackedSnapshot {
218    fn default() -> Self {
219        Self::new()
220    }
221}