use std::hash::{Hash, Hasher};
use bevy::prelude::*;
use crate::{
ChecksumFlag, ChecksumPart, RollbackId, RollbackOrdered, SaveWorld, SaveWorldSystems,
checksum_hasher,
};
pub struct ComponentChecksumPlugin<C: Component>(pub for<'a> fn(&'a C) -> u64);
fn default_hasher<C: Component + Hash>(component: &C) -> u64 {
let mut hasher = checksum_hasher();
component.hash(&mut hasher);
hasher.finish()
}
impl<C> Default for ComponentChecksumPlugin<C>
where
C: Component + Hash,
{
fn default() -> Self {
Self(default_hasher::<C>)
}
}
impl<C> Plugin for ComponentChecksumPlugin<C>
where
C: Component,
{
fn build(&self, app: &mut App) {
let custom_hasher = self.0;
let update = move |mut commands: Commands,
rollback_ordered: Res<RollbackOrdered>,
components: Query<
(&RollbackId, &C),
(With<RollbackId>, Without<ChecksumFlag<C>>),
>,
mut checksum: Query<
&mut ChecksumPart,
(Without<RollbackId>, With<ChecksumFlag<C>>),
>| {
let mut hasher = checksum_hasher();
let mut result = 0;
for (&rollback, component) in components.iter() {
let mut hasher = hasher;
rollback_ordered.order(rollback).hash(&mut hasher);
custom_hasher(component).hash(&mut hasher);
result ^= hasher.finish();
}
result.hash(&mut hasher);
let result = ChecksumPart(hasher.finish() as u128);
trace!(
"Component {} has checksum {:X}",
disqualified::ShortName::of::<C>(),
result.0
);
if let Ok(mut checksum) = checksum.single_mut() {
*checksum = result;
} else {
commands.spawn((result, ChecksumFlag::<C>::default()));
}
};
app.add_systems(SaveWorld, update.in_set(SaveWorldSystems::Checksum));
}
}