use crate::ecs::world::{CORE, UI, World, build_engine_ecs, register_core_components};
use freecs::EntityAllocator;
use freecs::dynamic::{ComponentRegistry, DynWorld, DynWorldSnapshot, SnapshotError};
#[derive(serde::Serialize, serde::Deserialize)]
pub struct WorldSnapshot {
pub allocator: EntityAllocator,
pub core: DynWorldSnapshot,
pub game_worlds: Vec<DynWorldSnapshot>,
}
fn clone_allocator(allocator: &EntityAllocator) -> EntityAllocator {
EntityAllocator {
next_id: allocator.next_id,
free_ids: allocator.free_ids.clone(),
slots: allocator.slots.clone(),
}
}
pub fn snapshot_world(world: &World) -> Result<WorldSnapshot, SnapshotError> {
let mut game_worlds = Vec::new();
for member in world.ecs.worlds.iter().skip(UI + 1) {
game_worlds.push(member.snapshot()?);
}
Ok(WorldSnapshot {
allocator: clone_allocator(&world.ecs.allocator),
core: world.ecs.worlds[CORE].snapshot()?,
game_worlds,
})
}
pub fn restore_world(
world: &mut World,
snapshot: &WorldSnapshot,
game_registries: Vec<ComponentRegistry>,
) -> Result<(), SnapshotError> {
if game_registries.len() != snapshot.game_worlds.len() {
return Err(SnapshotError::SchemaMismatch {
expected: format!("{} app member worlds", snapshot.game_worlds.len()),
found: format!("{} registries", game_registries.len()),
});
}
let mut core = DynWorld::from_snapshot(register_core_components(), &snapshot.core)?;
core.insert_missing_rows = true;
let mut ecs = build_engine_ecs();
ecs.allocator = clone_allocator(&snapshot.allocator);
ecs.worlds[CORE] = core;
for (registry, member_snapshot) in game_registries.into_iter().zip(&snapshot.game_worlds) {
let mut member = DynWorld::from_snapshot(registry, member_snapshot)?;
member.insert_missing_rows = true;
ecs.worlds.push(member);
}
world.ecs = ecs;
world.resources.hierarchy = freecs::dynamic::HierarchyIndex::default();
world.resources.commands.ecs.clear();
world.resources.commands.render.clear();
rebuild_entity_registry(world);
reset_retained_ui(world);
reconcile_texture_ledger(world);
world.resources.mesh_render_state.request_full_rebuild();
#[cfg(feature = "physics")]
reset_physics(world);
Ok(())
}
fn rebuild_entity_registry(world: &mut World) {
let mut names = std::collections::HashMap::new();
for (entity, name) in world.ecs.worlds[CORE]
.query_ref::<&crate::ecs::primitives::Name>()
.iter()
{
names.insert(name.0.clone(), entity);
}
let mut guid_index = std::collections::HashMap::new();
let mut highest_guid = 0u64;
for (entity, guid) in world.ecs.worlds[CORE]
.query_ref::<&crate::ecs::primitives::Guid>()
.iter()
{
guid_index.insert(guid.0, entity);
highest_guid = highest_guid.max(guid.0);
}
let registry = &mut world.resources.entities;
registry.names = names;
registry.guid_index = guid_index;
registry.tags.clear();
registry.next_guid = registry.next_guid.max(highest_guid.saturating_add(1));
}
fn reconcile_texture_ledger(world: &mut World) {
let World { ecs, resources } = world;
crate::render::wgpu::texture_cache::texture_cache_release_dead_entities(
&mut resources.texture_cache,
|entity| ecs.is_alive(crate::render_driver::scene_entity(entity)),
);
}
fn reset_retained_ui(world: &mut World) {
let retained_ui = &mut world.resources.retained_ui;
retained_ui.dirty.layout_dirty = true;
retained_ui.dirty.render_dirty = true;
retained_ui.interaction.focused_entity = None;
retained_ui.interaction.hovered_entity = None;
retained_ui.interaction.active_entity = None;
retained_ui.interaction.last_click = None;
}
#[cfg(feature = "assets")]
pub fn save_world_snapshot_to_bytes(world: &World) -> Result<Vec<u8>, SnapshotError> {
let snapshot = snapshot_world(world)?;
bincode::serialize(&snapshot).map_err(|error| SnapshotError::Codec(error.to_string()))
}
#[cfg(feature = "assets")]
pub fn load_world_snapshot_from_bytes(
world: &mut World,
bytes: &[u8],
game_registries: Vec<ComponentRegistry>,
) -> Result<(), SnapshotError> {
let snapshot: WorldSnapshot =
bincode::deserialize(bytes).map_err(|error| SnapshotError::Codec(error.to_string()))?;
restore_world(world, &snapshot, game_registries)
}
#[cfg(feature = "physics")]
fn reset_physics(world: &mut World) {
let physics = &mut world.resources.physics;
let gravity = physics.gravity;
let fixed_timestep = physics.fixed_timestep;
let max_substeps = physics.max_substeps;
let enabled = physics.enabled;
let debug_draw = physics.debug_draw;
*physics = crate::ecs::physics::resources::PhysicsWorld::default();
physics.gravity = gravity;
physics.fixed_timestep = fixed_timestep;
physics.max_substeps = max_substeps;
physics.enabled = enabled;
physics.debug_draw = debug_draw;
}