1use std::collections::HashMap;
14
15use crate::save::serializer::{DeserializeError, Serialize, SerializedValue};
16
17#[derive(Debug, Clone, PartialEq)]
23pub struct EntitySnapshot {
24 pub entity_id: u64,
26 pub components: HashMap<String, SerializedValue>,
28}
29
30impl EntitySnapshot {
31 pub fn new(entity_id: u64) -> Self {
33 Self { entity_id, components: HashMap::new() }
34 }
35
36 pub fn insert_component(&mut self, name: impl Into<String>, value: SerializedValue) {
38 self.components.insert(name.into(), value);
39 }
40
41 pub fn remove_component(&mut self, name: &str) -> Option<SerializedValue> {
43 self.components.remove(name)
44 }
45
46 pub fn get_component(&self, name: &str) -> Option<&SerializedValue> {
48 self.components.get(name)
49 }
50
51 pub fn has_component(&self, name: &str) -> bool {
53 self.components.contains_key(name)
54 }
55
56 pub fn component_count(&self) -> usize {
58 self.components.len()
59 }
60
61 pub fn merge_from(&mut self, other: &EntitySnapshot) {
63 for (k, v) in &other.components {
64 self.components.insert(k.clone(), v.clone());
65 }
66 }
67
68 pub fn to_serialized(&self) -> SerializedValue {
70 let mut map = HashMap::new();
71 map.insert("entity_id".into(), SerializedValue::Int(self.entity_id as i64));
72 let comp_map: HashMap<String, SerializedValue> = self.components.clone();
73 map.insert("components".into(), SerializedValue::Map(comp_map));
74 SerializedValue::Map(map)
75 }
76
77 pub fn from_serialized(v: &SerializedValue) -> Result<Self, DeserializeError> {
79 let id = v.get("entity_id")
80 .and_then(|v| v.as_int())
81 .ok_or_else(|| DeserializeError::MissingKey("entity_id".into()))? as u64;
82 let components = v.get("components")
83 .and_then(|v| v.as_map())
84 .cloned()
85 .unwrap_or_default();
86 Ok(Self { entity_id: id, components })
87 }
88}
89
90#[derive(Debug, Clone, PartialEq)]
96pub struct ResourceSnapshot {
97 pub type_name: String,
99 pub value: SerializedValue,
101}
102
103impl ResourceSnapshot {
104 pub fn new(type_name: impl Into<String>, value: SerializedValue) -> Self {
105 Self { type_name: type_name.into(), value }
106 }
107
108 pub fn to_serialized(&self) -> SerializedValue {
109 let mut map = HashMap::new();
110 map.insert("type_name".into(), SerializedValue::Str(self.type_name.clone()));
111 map.insert("value".into(), self.value.clone());
112 SerializedValue::Map(map)
113 }
114
115 pub fn from_serialized(v: &SerializedValue) -> Result<Self, DeserializeError> {
116 let type_name = v.get("type_name")
117 .and_then(|v| v.as_str())
118 .ok_or_else(|| DeserializeError::MissingKey("type_name".into()))?
119 .to_string();
120 let value = v.get("value").cloned().unwrap_or(SerializedValue::Null);
121 Ok(Self { type_name, value })
122 }
123}
124
125#[derive(Debug, Clone, Default)]
132pub struct SnapshotDiff {
133 pub added_entities: Vec<EntitySnapshot>,
135 pub removed_entities: Vec<u64>,
137 pub changed_entities: Vec<(u64, EntitySnapshot)>,
139 pub changed_resources: Vec<ResourceSnapshot>,
141 pub removed_resources: Vec<String>,
143}
144
145impl SnapshotDiff {
146 pub fn new() -> Self {
147 Self::default()
148 }
149
150 pub fn is_empty(&self) -> bool {
152 self.added_entities.is_empty()
153 && self.removed_entities.is_empty()
154 && self.changed_entities.is_empty()
155 && self.changed_resources.is_empty()
156 && self.removed_resources.is_empty()
157 }
158
159 pub fn entity_change_count(&self) -> usize {
161 self.added_entities.len() + self.removed_entities.len() + self.changed_entities.len()
162 }
163
164 pub fn resource_change_count(&self) -> usize {
166 self.changed_resources.len() + self.removed_resources.len()
167 }
168}
169
170#[derive(Debug, Clone)]
179pub struct WorldSnapshot {
180 pub entities: Vec<EntitySnapshot>,
182 pub resources: Vec<ResourceSnapshot>,
184 pub timestamp: f64,
186 pub version: u32,
188 pub metadata: HashMap<String, String>,
190}
191
192impl WorldSnapshot {
193 pub fn new() -> Self {
195 Self {
196 entities: Vec::new(),
197 resources: Vec::new(),
198 timestamp: 0.0,
199 version: 1,
200 metadata: HashMap::new(),
201 }
202 }
203
204 pub fn with_timestamp(mut self, ts: f64) -> Self {
206 self.timestamp = ts;
207 self
208 }
209
210 pub fn add_entity(&mut self, entity_id: u64, components: HashMap<String, SerializedValue>) {
214 if let Some(existing) = self.entities.iter_mut().find(|e| e.entity_id == entity_id) {
215 existing.components = components;
216 } else {
217 self.entities.push(EntitySnapshot { entity_id, components });
218 }
219 }
220
221 pub fn add_entity_snapshot(&mut self, snapshot: EntitySnapshot) {
223 self.add_entity(snapshot.entity_id, snapshot.components);
224 }
225
226 pub fn add_resource(&mut self, type_name: impl Into<String>, value: SerializedValue) {
228 let type_name = type_name.into();
229 if let Some(existing) = self.resources.iter_mut().find(|r| r.type_name == type_name) {
230 existing.value = value;
231 } else {
232 self.resources.push(ResourceSnapshot::new(type_name, value));
233 }
234 }
235
236 pub fn set_meta(&mut self, key: impl Into<String>, value: impl Into<String>) {
238 self.metadata.insert(key.into(), value.into());
239 }
240
241 pub fn get_meta(&self, key: &str) -> Option<&str> {
243 self.metadata.get(key).map(String::as_str)
244 }
245
246 pub fn entity_count(&self) -> usize {
250 self.entities.len()
251 }
252
253 pub fn resource_count(&self) -> usize {
255 self.resources.len()
256 }
257
258 pub fn get_entity(&self, id: u64) -> Option<&EntitySnapshot> {
260 self.entities.iter().find(|e| e.entity_id == id)
261 }
262
263 pub fn get_entity_mut(&mut self, id: u64) -> Option<&mut EntitySnapshot> {
265 self.entities.iter_mut().find(|e| e.entity_id == id)
266 }
267
268 pub fn get_resource(&self, type_name: &str) -> Option<&ResourceSnapshot> {
270 self.resources.iter().find(|r| r.type_name == type_name)
271 }
272
273 pub fn remove_entity(&mut self, id: u64) -> bool {
275 let before = self.entities.len();
276 self.entities.retain(|e| e.entity_id != id);
277 self.entities.len() < before
278 }
279
280 pub fn merge(&mut self, other: &WorldSnapshot) {
284 for entity in &other.entities {
285 self.add_entity(entity.entity_id, entity.components.clone());
286 }
287 for resource in &other.resources {
288 self.add_resource(resource.type_name.clone(), resource.value.clone());
289 }
290 for (k, v) in &other.metadata {
291 self.metadata.insert(k.clone(), v.clone());
292 }
293 if other.timestamp > self.timestamp {
295 self.timestamp = other.timestamp;
296 }
297 }
298
299 pub fn diff(&self, other: &WorldSnapshot) -> SnapshotDiff {
303 let mut diff = SnapshotDiff::new();
304
305 let old_map: HashMap<u64, &EntitySnapshot> =
307 self.entities.iter().map(|e| (e.entity_id, e)).collect();
308 let new_map: HashMap<u64, &EntitySnapshot> =
309 other.entities.iter().map(|e| (e.entity_id, e)).collect();
310
311 for (&id, &new_e) in &new_map {
313 match old_map.get(&id) {
314 None => diff.added_entities.push(new_e.clone()),
315 Some(&old_e) => {
316 if old_e != new_e {
317 diff.changed_entities.push((id, new_e.clone()));
318 }
319 }
320 }
321 }
322
323 for &id in old_map.keys() {
325 if !new_map.contains_key(&id) {
326 diff.removed_entities.push(id);
327 }
328 }
329
330 let old_res: HashMap<&str, &ResourceSnapshot> =
332 self.resources.iter().map(|r| (r.type_name.as_str(), r)).collect();
333 let new_res: HashMap<&str, &ResourceSnapshot> =
334 other.resources.iter().map(|r| (r.type_name.as_str(), r)).collect();
335
336 for (&name, &new_r) in &new_res {
337 match old_res.get(name) {
338 None => diff.changed_resources.push(new_r.clone()),
339 Some(&old_r) => {
340 if old_r != new_r {
341 diff.changed_resources.push(new_r.clone());
342 }
343 }
344 }
345 }
346
347 for &name in old_res.keys() {
348 if !new_res.contains_key(name) {
349 diff.removed_resources.push(name.to_string());
350 }
351 }
352
353 diff
354 }
355
356 pub fn apply_diff(&mut self, diff: &SnapshotDiff) {
360 for &id in &diff.removed_entities {
362 self.remove_entity(id);
363 }
364 for entity in &diff.added_entities {
366 self.add_entity_snapshot(entity.clone());
367 }
368 for (_, entity) in &diff.changed_entities {
370 self.add_entity_snapshot(entity.clone());
371 }
372 for &ref name in &diff.removed_resources {
374 self.resources.retain(|r| &r.type_name != name);
375 }
376 for resource in &diff.changed_resources {
377 self.add_resource(resource.type_name.clone(), resource.value.clone());
378 }
379 }
380}
381
382impl Default for WorldSnapshot {
383 fn default() -> Self {
384 Self::new()
385 }
386}
387
388pub struct SnapshotSerializer;
394
395impl SnapshotSerializer {
396 pub fn to_bytes(snapshot: &WorldSnapshot) -> Vec<u8> {
398 let sv = Self::snapshot_to_sv(snapshot);
399 sv.to_json_string().into_bytes()
400 }
401
402 pub fn from_bytes(bytes: &[u8]) -> Result<WorldSnapshot, DeserializeError> {
404 let s = std::str::from_utf8(bytes)
405 .map_err(|e| DeserializeError::ParseError(e.to_string()))?;
406 let sv = SerializedValue::from_json_str(s)?;
407 Self::snapshot_from_sv(&sv)
408 }
409
410 fn snapshot_to_sv(snapshot: &WorldSnapshot) -> SerializedValue {
411 let mut map = HashMap::new();
412 map.insert("version".into(), SerializedValue::Int(snapshot.version as i64));
413 map.insert("timestamp".into(), SerializedValue::Float(snapshot.timestamp));
414
415 let entities: Vec<SerializedValue> =
417 snapshot.entities.iter().map(|e| e.to_serialized()).collect();
418 map.insert("entities".into(), SerializedValue::List(entities));
419
420 let resources: Vec<SerializedValue> =
422 snapshot.resources.iter().map(|r| r.to_serialized()).collect();
423 map.insert("resources".into(), SerializedValue::List(resources));
424
425 let meta: HashMap<String, SerializedValue> = snapshot
427 .metadata
428 .iter()
429 .map(|(k, v)| (k.clone(), SerializedValue::Str(v.clone())))
430 .collect();
431 map.insert("metadata".into(), SerializedValue::Map(meta));
432
433 SerializedValue::Map(map)
434 }
435
436 fn snapshot_from_sv(sv: &SerializedValue) -> Result<WorldSnapshot, DeserializeError> {
437 let version = sv.get("version")
438 .and_then(|v| v.as_int())
439 .unwrap_or(1) as u32;
440 let timestamp = sv.get("timestamp")
441 .and_then(|v| v.as_float())
442 .unwrap_or(0.0);
443
444 let entities = sv.get("entities")
445 .and_then(|v| v.as_list())
446 .unwrap_or(&[])
447 .iter()
448 .map(EntitySnapshot::from_serialized)
449 .collect::<Result<Vec<_>, _>>()?;
450
451 let resources = sv.get("resources")
452 .and_then(|v| v.as_list())
453 .unwrap_or(&[])
454 .iter()
455 .map(ResourceSnapshot::from_serialized)
456 .collect::<Result<Vec<_>, _>>()?;
457
458 let metadata = sv.get("metadata")
459 .and_then(|v| v.as_map())
460 .map(|m| {
461 m.iter()
462 .filter_map(|(k, v)| v.as_str().map(|s| (k.clone(), s.to_string())))
463 .collect()
464 })
465 .unwrap_or_default();
466
467 Ok(WorldSnapshot { entities, resources, timestamp, version, metadata })
468 }
469}
470
471#[cfg(test)]
476mod tests {
477 use super::*;
478
479 fn make_entity(id: u64, hp: i64) -> EntitySnapshot {
480 let mut e = EntitySnapshot::new(id);
481 e.insert_component("health", SerializedValue::Int(hp));
482 e
483 }
484
485 #[test]
486 fn snapshot_add_entity() {
487 let mut snap = WorldSnapshot::new();
488 snap.add_entity(1, {
489 let mut m = HashMap::new();
490 m.insert("x".into(), SerializedValue::Float(10.0));
491 m
492 });
493 assert_eq!(snap.entity_count(), 1);
494 assert!(snap.get_entity(1).is_some());
495 }
496
497 #[test]
498 fn snapshot_replace_entity() {
499 let mut snap = WorldSnapshot::new();
500 snap.add_entity(1, {
501 let mut m = HashMap::new(); m.insert("hp".into(), SerializedValue::Int(100)); m
502 });
503 snap.add_entity(1, {
504 let mut m = HashMap::new(); m.insert("hp".into(), SerializedValue::Int(50)); m
505 });
506 assert_eq!(snap.entity_count(), 1);
507 assert_eq!(
508 snap.get_entity(1).unwrap().get_component("hp"),
509 Some(&SerializedValue::Int(50))
510 );
511 }
512
513 #[test]
514 fn snapshot_remove_entity() {
515 let mut snap = WorldSnapshot::new();
516 snap.add_entity(42, HashMap::new());
517 assert!(snap.remove_entity(42));
518 assert!(!snap.remove_entity(42));
519 }
520
521 #[test]
522 fn snapshot_diff_added_removed() {
523 let mut old = WorldSnapshot::new();
524 old.add_entity_snapshot(make_entity(1, 100));
525 old.add_entity_snapshot(make_entity(2, 50));
526
527 let mut new = WorldSnapshot::new();
528 new.add_entity_snapshot(make_entity(1, 80)); new.add_entity_snapshot(make_entity(3, 60)); let diff = old.diff(&new);
532 assert_eq!(diff.added_entities.len(), 1);
533 assert_eq!(diff.added_entities[0].entity_id, 3);
534 assert_eq!(diff.removed_entities, vec![2]);
535 assert_eq!(diff.changed_entities.len(), 1);
536 assert_eq!(diff.changed_entities[0].0, 1);
537 }
538
539 #[test]
540 fn snapshot_diff_no_changes() {
541 let mut snap = WorldSnapshot::new();
542 snap.add_entity_snapshot(make_entity(1, 100));
543 let diff = snap.diff(&snap.clone());
544 assert!(diff.is_empty());
545 }
546
547 #[test]
548 fn snapshot_merge() {
549 let mut base = WorldSnapshot::new();
550 base.add_entity_snapshot(make_entity(1, 100));
551
552 let mut patch = WorldSnapshot::new();
553 patch.add_entity_snapshot(make_entity(2, 200));
554 patch.timestamp = 99.0;
555
556 base.merge(&patch);
557 assert_eq!(base.entity_count(), 2);
558 assert_eq!(base.timestamp, 99.0);
559 }
560
561 #[test]
562 fn snapshot_serializer_roundtrip() {
563 let mut snap = WorldSnapshot::new();
564 snap.timestamp = 42.0;
565 snap.add_entity_snapshot(make_entity(7, 123));
566 snap.add_resource("score", SerializedValue::Int(9999));
567 snap.set_meta("level", "dungeon_1");
568
569 let bytes = SnapshotSerializer::to_bytes(&snap);
570 let restored = SnapshotSerializer::from_bytes(&bytes).unwrap();
571
572 assert_eq!(restored.timestamp, 42.0);
573 assert_eq!(restored.entity_count(), 1);
574 assert_eq!(restored.resource_count(), 1);
575 assert_eq!(restored.get_meta("level"), Some("dungeon_1"));
576 assert_eq!(
577 restored.get_entity(7).unwrap().get_component("health"),
578 Some(&SerializedValue::Int(123))
579 );
580 }
581
582 #[test]
583 fn resource_snapshot_roundtrip() {
584 let r = ResourceSnapshot::new("timer", SerializedValue::Float(3.14));
585 let sv = r.to_serialized();
586 let r2 = ResourceSnapshot::from_serialized(&sv).unwrap();
587 assert_eq!(r.type_name, r2.type_name);
588 }
589
590 #[test]
591 fn entity_snapshot_merge_from() {
592 let mut a = make_entity(1, 100);
593 let mut b = EntitySnapshot::new(1);
594 b.insert_component("mana", SerializedValue::Int(50));
595 a.merge_from(&b);
596 assert!(a.has_component("health"));
597 assert!(a.has_component("mana"));
598 assert_eq!(a.component_count(), 2);
599 }
600}