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}