1use std::{cell::RefCell, collections::HashMap, sync::Mutex};
2
3use once_cell::sync::Lazy;
4use serde::{Deserialize, Serialize};
5use shipyard::{AllStoragesViewMut, Component, EntityId, IntoIter, View, World};
6
7type Serializer = Box<dyn Fn(&World) -> HashMap<EntityId, Vec<u8>> + Send + Sync>;
8type Deserializer = Box<dyn Fn(&mut World, &HashMap<EntityId, Vec<u8>>) + Send + Sync>;
9
10#[derive(Default)]
11pub struct SnapshotRegistry {
12 components: HashMap<String, (Serializer, Deserializer)>,
13}
14
15impl SnapshotRegistry {
16 pub fn register<T: Component + Serialize + for<'de> Deserialize<'de> + 'static>(&mut self) {
18 let type_name = std::any::type_name::<T>().to_string();
19
20 let serializer: Serializer = Box::new(|world| {
21 let mut data = HashMap::new();
22 if let Ok(view) = world.borrow::<View<T>>() {
23 for (id, component) in view.iter().with_id() {
24 if let Ok(bytes) =
25 bincode::serde::encode_to_vec(component, bincode::config::standard())
26 {
27 data.insert(id, bytes);
28 }
29 }
30 }
31 data
32 });
33
34 let deserializer: Deserializer = Box::new(|world, data| {
35 let mut all_storages = world.borrow::<AllStoragesViewMut>().unwrap();
36 for bytes in data.values() {
37 if let Ok((c, _)) =
38 bincode::serde::decode_from_slice::<T, _>(bytes, bincode::config::standard())
39 {
40 all_storages.add_entity((c,));
41 }
42 }
43 });
44
45 self.components
46 .insert(type_name, (serializer, deserializer));
47 }
48}
49
50pub static REGISTRY: Lazy<Mutex<SnapshotRegistry>> =
52 Lazy::new(|| Mutex::new(SnapshotRegistry::default()));
53
54thread_local! {
55 static CURRENT_WORLD: RefCell<Option<*const World>> = const { RefCell::new(None) };
56}
57
58#[derive(Serialize, Deserialize, Clone)]
59pub struct SystemSnapshot {
60 pub system_name: String,
61 pub execution_time_ms: u64,
62 pub timestamp: u64,
63 pub component_data: HashMap<String, HashMap<EntityId, Vec<u8>>>,
64}
65
66impl SystemSnapshot {
67 pub fn capture_world(world: &World, system_name: &str, execution_time_ms: u64) -> Option<Self> {
68 let mut component_data = HashMap::new();
69 let registry = REGISTRY.lock().unwrap();
70
71 for (name, (serializer, _)) in ®istry.components {
72 component_data.insert(name.clone(), serializer(world));
73 }
74
75 Some(SystemSnapshot {
76 system_name: system_name.to_string(),
77 execution_time_ms,
78 timestamp: std::time::SystemTime::now()
79 .duration_since(std::time::UNIX_EPOCH)
80 .unwrap()
81 .as_secs(),
82 component_data,
83 })
84 }
85
86 pub fn save_to_file(&self) -> Result<(), anyhow::Error> {
87 std::fs::create_dir_all("snapshots")?;
88 let filename = format!("snapshots/{}_{}.snapshot", self.system_name, self.timestamp);
89 let data = bincode::serde::encode_to_vec(self, bincode::config::standard())?;
90 std::fs::write(filename, data)?;
91 Ok(())
92 }
93
94 pub fn load_from_file(path: &std::path::Path) -> Result<Self, anyhow::Error> {
95 let data = std::fs::read(path)?;
96 let (snapshot, _) = bincode::serde::decode_from_slice(&data, bincode::config::standard())?;
97 Ok(snapshot)
98 }
99
100 pub fn restore_world(&self) -> Result<World, anyhow::Error> {
101 let mut world = World::new();
102 let mut all_entities = std::collections::HashSet::new();
103 for component_map in self.component_data.values() {
104 for entity_id in component_map.keys() {
105 all_entities.insert(entity_id);
106 }
107 }
108
109 let registry = REGISTRY.lock().unwrap();
110 for (name, data) in &self.component_data {
111 if let Some((_, deserializer)) = registry.components.get(name) {
112 deserializer(&mut world, data);
113 }
114 }
115 Ok(world)
116 }
117}
118
119pub(crate) struct WorldGuard {
120 _private: (),
121}
122
123impl WorldGuard {
124 pub fn new(world: &World) -> Self {
125 CURRENT_WORLD.with(|current| {
126 *current.borrow_mut() = Some(world as *const World);
127 });
128 WorldGuard { _private: () }
129 }
130}
131
132impl Drop for WorldGuard {
133 fn drop(&mut self) {
134 CURRENT_WORLD.with(|current| {
135 *current.borrow_mut() = None;
136 });
137 }
138}
139
140pub fn with_current_world<F, R>(f: F) -> Option<R>
141where
142 F: FnOnce(&World) -> R,
143{
144 CURRENT_WORLD.with(|current| {
145 current.borrow().map(|ptr| {
146 let world = unsafe { &*ptr };
147 f(world)
148 })
149 })
150}
151
152pub trait WorldSnapshotExt {
153 fn run_default_workload_with_snapshot(&self) -> Result<(), shipyard::error::RunWorkload>;
154}
155
156impl WorldSnapshotExt for World {
157 fn run_default_workload_with_snapshot(&self) -> Result<(), shipyard::error::RunWorkload> {
158 let _guard = WorldGuard::new(self);
159 self.run_default_workload()
160 }
161}