tx2_link/
protocol.rs

1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4pub type EntityId = u32;
5pub type ComponentId = String;
6pub type FieldId = String;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
9#[repr(u8)]
10pub enum MessageType {
11    Snapshot = 0,
12    Delta = 1,
13    RequestSnapshot = 2,
14    Ack = 3,
15    Ping = 4,
16    Pong = 5,
17    SchemaSync = 6,
18    Error = 7,
19}
20
21#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct MessageHeader {
23    pub msg_type: MessageType,
24    pub timestamp: u64,
25    pub id: u64,
26    pub sequence: u64,
27    pub schema_version: u32,
28}
29
30impl MessageHeader {
31    pub fn new(msg_type: MessageType, schema_version: u32) -> Self {
32        use std::time::{SystemTime, UNIX_EPOCH};
33
34        static mut SEQUENCE_COUNTER: u64 = 0;
35        let sequence = unsafe {
36            SEQUENCE_COUNTER += 1;
37            SEQUENCE_COUNTER
38        };
39
40        let timestamp = SystemTime::now()
41            .duration_since(UNIX_EPOCH)
42            .unwrap()
43            .as_millis() as u64;
44
45        let id = (timestamp << 20) | (sequence & 0xFFFFF);
46
47        Self {
48            msg_type,
49            timestamp,
50            id,
51            sequence,
52            schema_version,
53        }
54    }
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize)]
58pub struct Message {
59    pub header: MessageHeader,
60    pub payload: MessagePayload,
61}
62
63#[derive(Debug, Clone, Serialize, Deserialize)]
64#[serde(tag = "type", rename_all = "snake_case")]
65pub enum MessagePayload {
66    Snapshot(SnapshotPayload),
67    Delta(DeltaPayload),
68    RequestSnapshot,
69    Ack { ack_id: u64 },
70    Ping,
71    Pong,
72    SchemaSync(SchemaSyncPayload),
73    Error { code: u32, message: String },
74}
75
76#[derive(Debug, Clone, Serialize, Deserialize)]
77pub struct SnapshotPayload {
78    pub entities: Vec<SerializedEntity>,
79    pub metadata: SnapshotMetadata,
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize)]
83pub struct SnapshotMetadata {
84    pub world_time: f64,
85    pub entity_count: u32,
86    pub component_count: u32,
87    pub compression: CompressionType,
88}
89
90#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
91#[repr(u8)]
92pub enum CompressionType {
93    None = 0,
94    Deflate = 1,
95    Lz4 = 2,
96    Zstd = 3,
97}
98
99#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct SerializedEntity {
101    pub id: EntityId,
102    pub components: Vec<SerializedComponent>,
103}
104
105#[derive(Debug, Clone, Serialize, Deserialize)]
106pub struct SerializedComponent {
107    pub id: ComponentId,
108    pub data: ComponentData,
109}
110
111#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
112pub enum ComponentData {
113    Binary(Vec<u8>),
114    Json(String),
115    Structured(HashMap<FieldId, FieldValue>),
116}
117
118impl ComponentData {
119    pub fn from_json_value(value: serde_json::Value) -> Self {
120        ComponentData::Json(value.to_string())
121    }
122
123    pub fn to_json_value(&self) -> Option<serde_json::Value> {
124        match self {
125            ComponentData::Json(s) => serde_json::from_str(s).ok(),
126            _ => None,
127        }
128    }
129
130    pub fn as_json_str(&self) -> Option<&str> {
131        match self {
132            ComponentData::Json(s) => Some(s),
133            _ => None,
134        }
135    }
136}
137
138#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
139pub enum FieldValue {
140    Null,
141    Bool(bool),
142    U8(u8),
143    U16(u16),
144    U32(u32),
145    U64(u64),
146    I8(i8),
147    I16(i16),
148    I32(i32),
149    I64(i64),
150    F32(f32),
151    F64(f64),
152    String(String),
153    Bytes(Vec<u8>),
154    Array(Vec<FieldValue>),
155    Map(HashMap<String, FieldValue>),
156}
157
158#[derive(Debug, Clone, Serialize, Deserialize)]
159pub struct DeltaPayload {
160    pub changes: Vec<DeltaChange>,
161    pub base_timestamp: u64,
162    pub metadata: DeltaMetadata,
163}
164
165#[derive(Debug, Clone, Serialize, Deserialize)]
166pub struct DeltaMetadata {
167    pub change_count: u32,
168    pub entities_added: u32,
169    pub entities_removed: u32,
170    pub components_updated: u32,
171}
172
173#[derive(Debug, Clone, Serialize, Deserialize)]
174#[serde(tag = "type", rename_all = "snake_case")]
175pub enum DeltaChange {
176    EntityAdded {
177        entity_id: EntityId,
178    },
179    EntityRemoved {
180        entity_id: EntityId,
181    },
182    ComponentAdded {
183        entity_id: EntityId,
184        component_id: ComponentId,
185        data: ComponentData,
186    },
187    ComponentRemoved {
188        entity_id: EntityId,
189        component_id: ComponentId,
190    },
191    ComponentUpdated {
192        entity_id: EntityId,
193        component_id: ComponentId,
194        data: ComponentData,
195    },
196    FieldsUpdated {
197        entity_id: EntityId,
198        component_id: ComponentId,
199        fields: Vec<FieldDelta>,
200    },
201}
202
203#[derive(Debug, Clone, Serialize, Deserialize)]
204pub struct FieldDelta {
205    pub field_id: FieldId,
206    pub old_value: Option<FieldValue>,
207    pub new_value: FieldValue,
208}
209
210#[derive(Debug, Clone, Serialize, Deserialize)]
211pub struct SchemaSyncPayload {
212    pub schemas: Vec<ComponentSchemaInfo>,
213}
214
215#[derive(Debug, Clone, Serialize, Deserialize)]
216pub struct ComponentSchemaInfo {
217    pub component_id: ComponentId,
218    pub version: u32,
219    pub fields: Vec<FieldSchemaInfo>,
220}
221
222#[derive(Debug, Clone, Serialize, Deserialize)]
223pub struct FieldSchemaInfo {
224    pub field_id: FieldId,
225    pub field_type: FieldType,
226    pub optional: bool,
227}
228
229#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
230#[repr(u8)]
231pub enum FieldType {
232    Null = 0,
233    Bool = 1,
234    U8 = 2,
235    U16 = 3,
236    U32 = 4,
237    U64 = 5,
238    I8 = 6,
239    I16 = 7,
240    I32 = 8,
241    I64 = 9,
242    F32 = 10,
243    F64 = 11,
244    String = 12,
245    Bytes = 13,
246    Array = 14,
247    Map = 15,
248}
249
250impl Message {
251    pub fn new(msg_type: MessageType, schema_version: u32, payload: MessagePayload) -> Self {
252        Self {
253            header: MessageHeader::new(msg_type, schema_version),
254            payload,
255        }
256    }
257
258    pub fn snapshot(entities: Vec<SerializedEntity>, world_time: f64, schema_version: u32) -> Self {
259        let entity_count = entities.len() as u32;
260        let component_count: u32 = entities.iter()
261            .map(|e| e.components.len() as u32)
262            .sum();
263
264        Self::new(
265            MessageType::Snapshot,
266            schema_version,
267            MessagePayload::Snapshot(SnapshotPayload {
268                entities,
269                metadata: SnapshotMetadata {
270                    world_time,
271                    entity_count,
272                    component_count,
273                    compression: CompressionType::None,
274                },
275            }),
276        )
277    }
278
279    pub fn delta(changes: Vec<DeltaChange>, base_timestamp: u64, schema_version: u32) -> Self {
280        let change_count = changes.len() as u32;
281        let entities_added = changes.iter()
282            .filter(|c| matches!(c, DeltaChange::EntityAdded { .. }))
283            .count() as u32;
284        let entities_removed = changes.iter()
285            .filter(|c| matches!(c, DeltaChange::EntityRemoved { .. }))
286            .count() as u32;
287        let components_updated = changes.iter()
288            .filter(|c| matches!(c, DeltaChange::ComponentUpdated { .. } | DeltaChange::FieldsUpdated { .. }))
289            .count() as u32;
290
291        Self::new(
292            MessageType::Delta,
293            schema_version,
294            MessagePayload::Delta(DeltaPayload {
295                changes,
296                base_timestamp,
297                metadata: DeltaMetadata {
298                    change_count,
299                    entities_added,
300                    entities_removed,
301                    components_updated,
302                },
303            }),
304        )
305    }
306
307    pub fn request_snapshot(schema_version: u32) -> Self {
308        Self::new(
309            MessageType::RequestSnapshot,
310            schema_version,
311            MessagePayload::RequestSnapshot,
312        )
313    }
314
315    pub fn ack(ack_id: u64, schema_version: u32) -> Self {
316        Self::new(
317            MessageType::Ack,
318            schema_version,
319            MessagePayload::Ack { ack_id },
320        )
321    }
322
323    pub fn ping(schema_version: u32) -> Self {
324        Self::new(MessageType::Ping, schema_version, MessagePayload::Ping)
325    }
326
327    pub fn pong(schema_version: u32) -> Self {
328        Self::new(MessageType::Pong, schema_version, MessagePayload::Pong)
329    }
330
331    pub fn error(code: u32, message: String, schema_version: u32) -> Self {
332        Self::new(
333            MessageType::Error,
334            schema_version,
335            MessagePayload::Error { code, message },
336        )
337    }
338}