use crate::{
input::GameInput,
sync::{SavedCell, SavedFrame},
Config, Event, Frame,
};
use std::{
collections::hash_map::DefaultHasher,
hash::{Hash, Hasher},
};
use tracing::{debug, error};
pub enum Command<T>
where
T: Config,
{
Save(SaveState<T::State>),
Load(LoadState<T::State>),
AdvanceFrame(GameInput<T::Input>),
Event(Event),
}
pub struct SaveState<T> {
pub(crate) cell: SavedCell<T>,
pub(crate) frame: Frame,
}
impl<T: Clone> SaveState<T> {
pub fn save(self, state: T)
where
T: Hash,
{
let mut hasher = DefaultHasher::new();
state.hash(&mut hasher);
self.save_with_hash(state, hasher.finish());
}
pub fn save_without_hash(self, state: T) {
self.save_state(state, None);
}
pub fn save_with_hash(self, state: T, checksum: u64) {
self.save_state(state, Some(checksum));
}
fn save_state(self, state: T, checksum: Option<u64>) {
debug!(
"=== Saved frame state {} (checksum: {:08x}).",
self.frame,
checksum.unwrap_or(0)
);
self.cell.save(SavedFrame::<T> {
frame: self.frame,
data: Some(Box::new(state)),
checksum,
});
assert!(self.cell.is_valid());
}
}
impl<T> Drop for SaveState<T> {
fn drop(&mut self) {
if !self.cell.is_valid() {
error!("A SaveState command was dropped without saving a valid state.");
}
}
}
pub struct LoadState<T> {
pub(crate) cell: SavedCell<T>,
}
impl<T: Clone> LoadState<T> {
pub fn load(self) -> T {
self.cell.load()
}
}
pub struct Commands<T>
where
T: Config,
{
commands: Vec<Command<T>>,
}
impl<T: Config> Commands<T> {
pub(crate) fn push(&mut self, command: Command<T>) {
self.commands.push(command);
}
}
impl<T: Config> Default for Commands<T> {
fn default() -> Self {
Self {
commands: Vec::new(),
}
}
}
impl<T: Config> IntoIterator for Commands<T> {
type Item = Command<T>;
type IntoIter = std::vec::IntoIter<Command<T>>;
fn into_iter(self) -> Self::IntoIter {
self.commands.into_iter()
}
}