use gizmo_core::World;
#[cfg(target_arch = "wasm32")]
use web_time::Instant;
#[cfg(not(target_arch = "wasm32"))]
use std::time::Instant;
#[derive(Clone)]
pub struct EntitySnapshot {
pub entity_id: u32,
pub name: Option<String>,
pub components: std::collections::BTreeMap<String, String>,
}
#[derive(Clone)]
pub struct SceneSnapshot {
pub entities: Vec<EntitySnapshot>,
pub timestamp: Instant,
}
impl SceneSnapshot {
pub fn capture(
world: &World,
registry: &crate::registry::SceneRegistry,
protected_ids: &std::collections::HashSet<u32>,
) -> Self {
let mut entities = Vec::new();
let names = world.borrow::<gizmo_core::EntityName>();
for ent in world.iter_alive_entities() {
let id = ent.id();
if protected_ids.contains(&id) {
continue;
}
if let Some(name) = names.get(id) {
if name.0.starts_with("Editor ") || name.0 == "Highlight Box" {
continue;
}
}
let name = names.get(id).map(|n| n.0.clone());
let mut components = std::collections::BTreeMap::new();
let entity = gizmo_core::entity::Entity::new(id, 0);
let types = world.get_entity_component_types(entity);
for type_id in types {
if let Some(reg) = registry.get_registration(type_id) {
if let (Some(ptr), Some(get_reflect_ptr)) = (world.get_component_ptr(entity, type_id), reg.get_reflect_ptr_fn) {
let reflect_ptr = get_reflect_ptr(ptr);
let reflect_val = unsafe { &*reflect_ptr };
let type_reg = registry.reflect_registry.get(type_id);
if let Some(_type_registration) = type_reg {
let serializer = bevy_reflect::serde::TypedReflectSerializer::new(reflect_val, ®istry.reflect_registry);
if let Ok(string_repr) = ron::ser::to_string(&serializer) {
components.insert(reg.name.clone(), string_repr);
}
} else if let Some(ser_fn) = reg.serialize_fn {
if let Ok(string_repr) = ser_fn(ptr) {
components.insert(reg.name.clone(), string_repr);
}
}
}
}
}
if name.is_some() || !components.is_empty() {
entities.push(EntitySnapshot {
entity_id: id,
name,
components,
});
}
}
Self {
entities,
timestamp: Instant::now(),
}
}
pub fn restore(
&self,
world: &mut World,
registry: &crate::registry::SceneRegistry,
protected_ids: &std::collections::HashSet<u32>,
) -> RestoreResult {
let start = Instant::now();
let mut restored_count = 0u32;
let mut despawned_count = 0u32;
let mut snap_ids = std::collections::HashSet::new();
for snap_ent in &self.entities {
snap_ids.insert(snap_ent.entity_id);
}
let alive = world.iter_alive_entities();
let mut to_despawn = Vec::new();
{
let names = world.borrow::<gizmo_core::EntityName>();
for ent in &alive {
let id = ent.id();
if protected_ids.contains(&id) {
continue;
}
if let Some(name) = names.get(id) {
if name.0.starts_with("Editor ") || name.0 == "Highlight Box" {
continue;
}
}
if !snap_ids.contains(&id) {
to_despawn.push(*ent);
}
}
}
for ent in to_despawn {
world.despawn(ent);
despawned_count += 1;
}
for snap_entity in &self.entities {
let ent = if let Some(e) = world.get_entity(snap_entity.entity_id) {
if world.is_alive(e) {
e
} else {
world.spawn()
}
} else {
world.spawn()
};
if let Some(ref name) = snap_entity.name {
world.add_component(ent, gizmo_core::EntityName::new(name));
}
for (comp_name, comp_val) in &snap_entity.components {
if let Some(type_id) = registry.get_type_id(comp_name) {
if let Some(reg) = registry.get_registration(type_id) {
if let Some(type_reg) = registry.reflect_registry.get(type_id) {
let deserializer = bevy_reflect::serde::TypedReflectDeserializer::new(type_reg, ®istry.reflect_registry);
if let Ok(mut de) = ron::de::Deserializer::from_str(comp_val) {
if let Ok(reflect_val) = serde::de::DeserializeSeed::deserialize(deserializer, &mut de) {
if let Some(insert_fn) = reg.insert_reflect_fn {
let _ = insert_fn(world, ent, &*reflect_val);
}
}
}
} else if let Some(deserialize_fn) = reg.deserialize_fn {
let _ = deserialize_fn(world, ent, comp_val);
}
}
}
}
restored_count += 1;
}
RestoreResult {
despawned: despawned_count,
restored: restored_count,
duration: start.elapsed(),
}
}
pub fn entity_count(&self) -> usize {
self.entities.len()
}
pub fn age(&self) -> std::time::Duration {
self.timestamp.elapsed()
}
}
#[derive(Debug, Clone)]
pub struct RestoreResult {
pub despawned: u32,
pub restored: u32,
pub duration: std::time::Duration,
}
impl std::fmt::Display for RestoreResult {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Restore: {} entity silindi, {} entity geri yüklendi ({:.2}ms)",
self.despawned,
self.restored,
self.duration.as_secs_f64() * 1000.0,
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_scene_snapshot_capture_empty_world() {
let world = World::new();
let registry = crate::registry::SceneRegistry::new();
let protected = std::collections::HashSet::new();
let snapshot = SceneSnapshot::capture(&world, ®istry, &protected);
assert_eq!(snapshot.entity_count(), 0);
}
#[test]
fn test_scene_snapshot_round_trip() {
let mut world = World::new();
let registry = crate::registry::default_scene_registry();
let protected = std::collections::HashSet::new();
let ent = world.spawn();
world.add_component(ent, gizmo_core::EntityName::new("TestCube"));
world.add_component(
ent,
gizmo_physics_core::Transform::new(gizmo_math::Vec3::new(1.0, 2.0, 3.0)),
);
let snapshot = SceneSnapshot::capture(&world, ®istry, &protected);
assert_eq!(snapshot.entity_count(), 1);
assert_eq!(snapshot.entities[0].name.as_deref(), Some("TestCube"));
assert!(snapshot.entities[0].components.contains_key("Transform"));
world.despawn(ent);
assert_eq!(world.iter_alive_entities().len(), 0);
let result = snapshot.restore(&mut world, ®istry, &protected);
assert_eq!(result.restored, 1);
let names = world.borrow::<gizmo_core::EntityName>();
let alive = world.iter_alive_entities();
assert_eq!(alive.len(), 1);
let restored_name = names.get(alive[0].id());
assert!(restored_name.is_some());
assert_eq!(restored_name.unwrap().0, "TestCube");
}
}