use std::collections::{BTreeMap, HashSet};
use bevy_ecs::prelude::*;
use bevy_reflect::TypePath;
use rig_core::{
effect::{HandlerDescriptor, HandlerKey},
serve::ErasedHandler,
};
use super::{Checkpoint, CheckpointError, Loaded};
use crate::bus::{Bound, HandlerIndex, handlers::HandlerTable};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RestoreMode {
Strict,
Replace,
}
impl Checkpoint {
pub fn requirements(&self) -> Result<Vec<HandlerDescriptor>, CheckpointError> {
let mut keys = HashSet::new();
let mut requirements = Vec::new();
for (row, entity) in self.entities.iter().enumerate() {
if let Some(value) = entity.get(Bound::type_path()) {
let bound = super::saved_bound(row, value)?;
if bound.key != bound.descriptor.key {
return Err(CheckpointError::HandlerKeyMismatch { key: bound.key });
}
if !keys.insert(bound.key.clone()) {
return Err(CheckpointError::DuplicateSavedHandler { key: bound.key });
}
requirements.push(bound.descriptor);
}
}
requirements.sort_by(|a, b| a.key.cmp(&b.key));
Ok(requirements)
}
}
pub fn load_world(
checkpoint: &Checkpoint,
world: &mut World,
mode: RestoreMode,
handlers: impl IntoIterator<Item = (HandlerKey, ErasedHandler)>,
) -> Result<Loaded, CheckpointError> {
let requirements = checkpoint.requirements()?;
let (assets, aliases) = super::validated_state(checkpoint, world)?;
let mut supplied = BTreeMap::new();
for (key, handler) in handlers {
if supplied.insert(key.clone(), handler).is_some() {
return Err(CheckpointError::DuplicateSuppliedHandler { key });
}
}
let index = world
.get_resource::<HandlerIndex>()
.ok_or(CheckpointError::NotInstalled("handler index"))?;
let table = world
.get_non_send::<HandlerTable>()
.ok_or(CheckpointError::NotInstalled("handler table"))?;
let mut descriptors = BTreeMap::new();
for saved in &requirements {
let actual = if let Some(handler) = supplied.get(&saved.key) {
let mut descriptor = handler.descriptor();
descriptor.key = saved.key.clone();
descriptor
} else {
index
.entity(&saved.key)
.filter(|entity| table.contains(*entity))
.and_then(|entity| world.get::<Bound>(entity))
.ok_or_else(|| CheckpointError::MissingHandler {
key: saved.key.clone(),
})?
.descriptor
.clone()
};
if actual.family.family() != saved.family.family() {
return Err(CheckpointError::FamilyChanged {
key: saved.key.clone(),
saved: saved.family.family(),
found: actual.family.family(),
});
}
if mode == RestoreMode::Strict && actual != *saved {
return Err(CheckpointError::DescriptorChanged {
key: saved.key.clone(),
});
}
descriptors.insert(saved.key.clone(), actual);
}
for key in supplied.keys() {
if !descriptors.contains_key(key) {
return Err(CheckpointError::UnrequiredHandler { key: key.clone() });
}
}
let loaded = super::load_state(checkpoint, world, assets, aliases)?;
for entity in &loaded.entities {
let Some(bound) = world.get::<Bound>(*entity) else {
continue;
};
let key = bound.key.clone();
if let Some(descriptor) = descriptors.remove(&key) {
world.entity_mut(*entity).insert(Bound {
key: key.clone(),
descriptor,
});
}
if let Some(handler) = supplied.remove(&key) {
HandlerTable::install(world, *entity, handler);
}
}
Ok(loaded)
}