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);
}
std::mem::swap(&mut ecs.resources, &mut world.ecs.resources);
world.ecs = ecs;
*world.res_mut::<freecs::dynamic::HierarchyIndex>() =
freecs::dynamic::HierarchyIndex::default();
world
.res_mut::<crate::ecs::world::commands::CommandQueues>()
.ecs
.clear();
world
.res_mut::<crate::ecs::world::commands::CommandQueues>()
.render
.clear();
rebuild_entity_registry(world);
reset_retained_ui(world);
reconcile_texture_ledger(world);
world
.res_mut::<crate::render::mesh_state::MeshRenderState>()
.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.res_mut::<crate::ecs::entity_registry::EntityRegistry>();
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 mut texture_cache =
std::mem::take(world.res_mut::<crate::render::wgpu::texture_cache::TextureCache>());
crate::render::wgpu::texture_cache::texture_cache_release_dead_entities(
&mut texture_cache,
|entity| world.is_alive(crate::render_driver::scene_entity(entity)),
);
*world.res_mut::<crate::render::wgpu::texture_cache::TextureCache>() = texture_cache;
}
fn reset_retained_ui(world: &mut World) {
let retained_ui = &mut world.res_mut::<crate::ecs::ui::resources::RetainedUiState>();
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) {
use crate::plugins::physics::resources::PhysicsWorld;
let physics = world.plugin_resource_mut::<PhysicsWorld>();
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::plugins::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;
}